├── .gitignore ├── NRedisTimeSeries.Example ├── NRedisTimeSeries.Example.csproj ├── GetExample.cs ├── GetExampleAsync.cs ├── RulesExampleAsync.cs ├── RulesExample.cs ├── AlterExample.cs ├── AlterExampleAsync.cs ├── MAddExample.cs ├── MAddExampleAsync.cs ├── InfoQueryIndexExample.cs ├── CreateExample.cs ├── InfoQueryIndexExampleAsync.cs ├── CreateExampleAsync.cs ├── MGetExample.cs ├── MGetExampleAsync.cs ├── IncrByExample.cs ├── DecrByExample.cs ├── IncrByExampleAsync.cs ├── DecrByExampleAsync.cs ├── AddExample.cs ├── AddExampleAsync.cs ├── RangeExample.cs ├── RangeExampleAsync.cs ├── MRangeExample.cs └── MRangeExampleAsync.cs ├── NRedisTimeSeries.Test ├── TestAPI │ ├── RedisFixture.cs │ ├── TestQueryIndexAsync.cs │ ├── TestQueryIndex.cs │ ├── TestGet.cs │ ├── TestGetAsync.cs │ ├── TestAlterAsync.cs │ ├── TestAlter.cs │ ├── TestDel.cs │ ├── TestDelAsync.cs │ ├── AbstractTimeSeriesTest.cs │ ├── TestMGetAsync.cs │ ├── TestMGet.cs │ ├── TestIncrBy.cs │ ├── TestDecrBy.cs │ ├── TestRulesAsync.cs │ ├── TestCreate.cs │ ├── TestMAddAsync.cs │ ├── TestMADD.cs │ ├── TestCreateAsync.cs │ ├── TestIncrByAsync.cs │ ├── TestDecrByAsync.cs │ ├── TestRules.cs │ ├── TestRange.cs │ ├── TestRevRange.cs │ ├── TestRangeAsync.cs │ ├── TestRevRangeAsync.cs │ └── TestAdd.cs ├── NRedisTimeSeries.Test.csproj └── TestDataTypes │ ├── TestTimeStamp.cs │ ├── TestTimeSeriesInformation.cs │ ├── TestTimeSeriesTuple.cs │ ├── TestTimeSeriesLabel.cs │ └── TestTimeSeriesRule.cs ├── .github ├── release-drafter-config.yml └── workflows │ └── release-drafter.yml ├── NRedisTimeSeries ├── Commands │ ├── Enums │ │ ├── Reduce.cs │ │ ├── DuplicatePolicy.cs │ │ └── Aggregation.cs │ ├── Commands.cs │ └── CommandArgs.cs ├── Extensions │ ├── ReduceExtensions.cs │ ├── DuplicatePolicyExtensions.cs │ └── AggregationExtensions.cs ├── NRedisTimeSeries.csproj ├── DataTypes │ ├── TimeSeriesLabel.cs │ ├── TimeSeriesTuple.cs │ ├── TimeSeriesRule.cs │ ├── TimeSeriesInformation.cs │ └── TimeStamp.cs └── TimeSeriesClientResponseParser.cs ├── LICENSE ├── README.md └── NRedisTimeSeries.sln /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | .vscode/ 3 | Docs/ 4 | **/bin 5 | **/obj 6 | NRedisTimeSeries.Test/TestResults/ 7 | 8 | # JetBrains related IDEs 9 | .idea/ 10 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/NRedisTimeSeries.Example.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 1.4.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/RedisFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using StackExchange.Redis; 3 | 4 | namespace NRedisTimeSeries.Test.TestAPI 5 | { 6 | public class RedisFixture : IDisposable 7 | { 8 | public RedisFixture() => Redis = ConnectionMultiplexer.Connect("localhost"); 9 | 10 | public void Dispose() 11 | { 12 | Redis.Close(); 13 | } 14 | 15 | public ConnectionMultiplexer Redis { get; private set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.github/release-drafter-config.yml: -------------------------------------------------------------------------------- 1 | name-template: 'Version $NEXT_PATCH_VERSION' 2 | tag-template: 'v$NEXT_PATCH_VERSION' 3 | categories: 4 | - title: 'Features' 5 | labels: 6 | - 'feature' 7 | - 'enhancement' 8 | - title: 'Bug Fixes' 9 | labels: 10 | - 'fix' 11 | - 'bugfix' 12 | - 'bug' 13 | - title: 'Maintenance' 14 | label: 'chore' 15 | change-template: '- $TITLE (#$NUMBER)' 16 | exclude-labels: 17 | - 'skip-changelog' 18 | template: | 19 | ## Changes 20 | 21 | $CHANGES 22 | -------------------------------------------------------------------------------- /NRedisTimeSeries/Commands/Enums/Reduce.cs: -------------------------------------------------------------------------------- 1 | namespace NRedisTimeSeries.Commands.Enums 2 | { 3 | /// 4 | /// TODO: Add description 5 | /// 6 | public enum TsReduce 7 | { 8 | /// 9 | /// A sum of all samples in the group 10 | /// 11 | Sum, 12 | 13 | /// 14 | /// A minimum sample of all samples in the group 15 | /// 16 | Min, 17 | 18 | /// 19 | /// A maximum sample of all samples in the group 20 | /// 21 | Max, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - master 8 | 9 | jobs: 10 | update_release_draft: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # Drafts your next Release notes as Pull Requests are merged into "master" 14 | - uses: release-drafter/release-drafter@v5 15 | with: 16 | # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml 17 | config-name: release-drafter-config.yml 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/NRedisTimeSeries.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | false 6 | 1.4.0 7 | 8 | 9 | 10 | 11 | 12 | 13 | runtime; build; native; contentfiles; analyzers; buildtransitive 14 | all 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/GetExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NRedisTimeSeries.DataTypes; 3 | using StackExchange.Redis; 4 | 5 | namespace NRedisTimeSeries.Example 6 | { 7 | /// 8 | /// Examples for NRedisTimeSeries API for GET queries. 9 | /// 10 | internal class GetExample 11 | { 12 | /// 13 | /// Example for GET query. The NRedisTimeSeries TimeSeriesGet command returns a TimeSeriesTuple object. 14 | /// 15 | public static void SimpleGetExample() 16 | { 17 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 18 | IDatabase db = redis.GetDatabase(); 19 | TimeSeriesTuple value = db.TimeSeriesGet("my_ts"); 20 | Console.WriteLine(value); 21 | redis.Close(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /NRedisTimeSeries/Extensions/ReduceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NRedisTimeSeries.Commands.Enums; 3 | 4 | namespace NRedisTimeSeries.Extensions 5 | { 6 | internal static class ReduceExtensions 7 | { 8 | public static string AsArg(this TsReduce reduce) => reduce switch 9 | { 10 | TsReduce.Sum => "SUM", 11 | TsReduce.Min => "MIN", 12 | TsReduce.Max => "MAX", 13 | _ => throw new ArgumentOutOfRangeException(nameof(reduce), "Invalid Reduce type"), 14 | }; 15 | 16 | public static TsReduce AsReduce(string reduce) => reduce switch 17 | { 18 | "SUM" => TsReduce.Sum, 19 | "MIN" => TsReduce.Min, 20 | "MAX" => TsReduce.Max, 21 | _ => throw new ArgumentOutOfRangeException(nameof(reduce), $"Invalid Reduce type '{reduce}'"), 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestDataTypes/TestTimeStamp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NRedisTimeSeries.DataTypes; 3 | using Xunit; 4 | namespace NRedisTimeSeries.Test 5 | { 6 | public class TestTimeStamp 7 | { 8 | 9 | [Fact] 10 | public void TestTimeStampImplicitCast() 11 | { 12 | TimeStamp ts = 1; 13 | Assert.Equal(1, ts); 14 | 15 | ts = "+"; 16 | Assert.Equal("+", ts); 17 | 18 | ts = "*"; 19 | Assert.Equal("*", ts); 20 | 21 | ts = "-"; 22 | Assert.Equal("-", ts); 23 | 24 | var ex = Assert.Throws(() => ts = "hi"); 25 | Assert.Equal("The string hi cannot be used", ex.Message); 26 | 27 | DateTime now = DateTime.UtcNow; 28 | ts = now; 29 | Assert.Equal(now, ts); 30 | 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/GetExampleAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using StackExchange.Redis; 3 | using System.Threading.Tasks; 4 | using System; 5 | 6 | namespace NRedisTimeSeries.Example 7 | { 8 | /// 9 | /// Examples for NRedisTimeSeries async API for GET queries. 10 | /// 11 | internal class GetAsyncExample 12 | { 13 | /// 14 | /// Example for GET query. The NRedisTimeSeries TimeSeriesGet command returns a TimeSeriesTuple object. 15 | /// 16 | public static async Task SimpleGetAsyncExample() 17 | { 18 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 19 | IDatabase db = redis.GetDatabase(); 20 | TimeSeriesTuple value = await db.TimeSeriesGetAsync("my_ts"); 21 | Console.WriteLine(value); 22 | redis.Close(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /NRedisTimeSeries/NRedisTimeSeries.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 8.0 6 | 1.4.0 7 | Dvir Dukhan 8 | Redis Labs 9 | .Net Client for RedisTimeSeries 10 | 1.4.0 11 | 12 | 13 | 14 | 15 | 16 | 17 | ../Docs/NRedisTimeSeries.xml 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /NRedisTimeSeries/Commands/Commands.cs: -------------------------------------------------------------------------------- 1 | namespace NRedisTimeSeries.Commands 2 | { 3 | internal class TS 4 | { 5 | public static string CREATE => "TS.CREATE"; 6 | public static string ALTER => "TS.ALTER"; 7 | public static string ADD => "TS.ADD"; 8 | public static string MADD => "TS.MADD"; 9 | public static string INCRBY => "TS.INCRBY"; 10 | public static string DECRBY => "TS.DECRBY"; 11 | public static string DEL => "TS.DEL"; 12 | public static string CREATERULE => "TS.CREATERULE"; 13 | public static string DELETERULE => "TS.DELETERULE"; 14 | public static string RANGE => "TS.RANGE"; 15 | public static string REVRANGE => "TS.REVRANGE"; 16 | public static string MRANGE => "TS.MRANGE"; 17 | public static string MREVRANGE => "TS.MREVRANGE"; 18 | public static string GET => "TS.GET"; 19 | public static string MGET => "TS.MGET"; 20 | public static string INFO => "TS.INFO"; 21 | public static string QUERYINDEX => "TS.QUERYINDEX"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/RulesExampleAsync.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using StackExchange.Redis; 3 | using NRedisTimeSeries.Commands.Enums; 4 | using NRedisTimeSeries.DataTypes; 5 | 6 | namespace NRedisTimeSeries.Example 7 | { 8 | /// 9 | /// Examples for NRedisTimeSeries async API creating and deleting rules. 10 | /// 11 | internal class RulesAsyncExample 12 | { 13 | /// 14 | /// Example for create and delete RedisTimeSeries rules. 15 | /// 16 | public static async Task RulesCreateDeleteAsyncExample() 17 | { 18 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 19 | IDatabase db = redis.GetDatabase(); 20 | // Create your rule with destination key, time bucket and aggregation type. 21 | TimeSeriesRule rule = new TimeSeriesRule("dest_ts", 50, TsAggregation.Avg); 22 | await db.TimeSeriesCreateRuleAsync("my_ts", rule); 23 | await db.TimeSeriesDeleteRuleAsync("my_ts", "dest"); 24 | redis.Close(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /NRedisTimeSeries/Commands/CommandArgs.cs: -------------------------------------------------------------------------------- 1 | namespace NRedisTimeSeries.Commands 2 | { 3 | internal class CommandArgs 4 | { 5 | public static string RETENTION => "RETENTION"; 6 | public static string LABELS => "LABELS"; 7 | public static string UNCOMPRESSED => "UNCOMPRESSED"; 8 | public static string COUNT => "COUNT"; 9 | public static string AGGREGATION => "AGGREGATION"; 10 | public static string ALIGN => "ALIGN"; 11 | public static string FILTER => "FILTER"; 12 | public static string WITHLABELS => "WITHLABELS"; 13 | public static string SELECTEDLABELS => "SELECTED_LABELS"; 14 | public static string TIMESTAMP => "TIMESTAMP"; 15 | public static string CHUNK_SIZE => "CHUNK_SIZE"; 16 | public static string DUPLICATE_POLICY => "DUPLICATE_POLICY"; 17 | public static string ON_DUPLICATE => "ON_DUPLICATE"; 18 | public static string GROPUBY => "GROUPBY"; 19 | public static string REDUCE => "REDUCE"; 20 | public static string FILTER_BY_TS => "FILTER_BY_TS"; 21 | public static string FILTER_BY_VALUE => "FILTER_BY_VALUE"; 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/RulesExample.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.Commands.Enums; 2 | using NRedisTimeSeries.DataTypes; 3 | using StackExchange.Redis; 4 | 5 | namespace NRedisTimeSeries.Example 6 | { 7 | /// 8 | /// Example for create and delete RedisTimeSeries rules. 9 | /// 10 | internal class RulesExample 11 | { 12 | /// 13 | /// Example for create and delete RedisTimeSeries rules. 14 | /// 15 | public static void RulesCreateDeleteExample() 16 | { 17 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 18 | IDatabase db = redis.GetDatabase(); 19 | // Create new timeseries for the sourceKey and the destKey. 20 | db.TimeSeriesCreate("my_ts"); 21 | db.TimeSeriesCreate("dest_ts"); 22 | // Create your rule with destination key, time bucket and aggregation type. 23 | TimeSeriesRule rule = new TimeSeriesRule("dest_ts", 50, TsAggregation.Avg); 24 | db.TimeSeriesCreateRule("my_ts", rule); 25 | db.TimeSeriesDeleteRule("my_ts", "dest_ts"); 26 | redis.Close(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/AlterExample.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NRedisTimeSeries.DataTypes; 3 | using StackExchange.Redis; 4 | 5 | namespace NRedisTimeSeries.Example 6 | { 7 | /// 8 | /// Examples for NRedisTimeSeries API for altering time series properties. 9 | /// 10 | internal class AlterExample 11 | { 12 | /// 13 | /// Examples for altering time-series. 14 | /// The parameters retentionTime, and labels are optional and can be set in any order when used as named argument. 15 | /// 16 | public static void ParameterizedAlter() 17 | { 18 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 19 | IDatabase db = redis.GetDatabase(); 20 | var label = new TimeSeriesLabel("key", "value"); 21 | var labels = new List { label }; 22 | db.TimeSeriesAlter("labeld_ts", labels: labels); 23 | db.TimeSeriesAlter("retention_time_ts", retentionTime: 5000); 24 | db.TimeSeriesAlter("parameterized_ts", retentionTime: 5000, labels: labels); 25 | redis.Close(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestQueryIndexAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Xunit; 5 | 6 | namespace NRedisTimeSeries.Test.TestAPI 7 | { 8 | public class TestQueryIndexAsync : AbstractTimeSeriesTest 9 | { 10 | public TestQueryIndexAsync(RedisFixture redisFixture) : base(redisFixture) { } 11 | 12 | [Fact] 13 | public async Task TestTSQueryIndex() 14 | { 15 | var keys = CreateKeyNames(2); 16 | var db = redisFixture.Redis.GetDatabase(); 17 | var label1 = new TimeSeriesLabel(keys[0], "value"); 18 | var label2 = new TimeSeriesLabel(keys[1], "value2"); 19 | var labels1 = new List { label1, label2 }; 20 | var labels2 = new List { label1 }; 21 | 22 | await db.TimeSeriesCreateAsync(keys[0], labels: labels1); 23 | await db.TimeSeriesCreateAsync(keys[1], labels: labels2); 24 | Assert.Equal(keys, db.TimeSeriesQueryIndex(new List { $"{keys[0]}=value" })); 25 | Assert.Equal(new List { keys[0] }, db.TimeSeriesQueryIndex(new List { $"{keys[1]}=value2" })); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /NRedisTimeSeries/Extensions/DuplicatePolicyExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NRedisTimeSeries.Commands.Enums; 3 | 4 | namespace NRedisTimeSeries.Extensions 5 | { 6 | internal static class DuplicatePolicyExtensions 7 | { 8 | public static string AsArg(this TsDuplicatePolicy policy) => policy switch 9 | { 10 | TsDuplicatePolicy.BLOCK => "BLOCK", 11 | TsDuplicatePolicy.FIRST => "FIRST", 12 | TsDuplicatePolicy.LAST => "LAST", 13 | TsDuplicatePolicy.MIN => "MIN", 14 | TsDuplicatePolicy.MAX => "MAX", 15 | TsDuplicatePolicy.SUM => "SUM", 16 | _ => throw new ArgumentOutOfRangeException(nameof(policy), "Invalid policy type"), 17 | }; 18 | 19 | public static TsDuplicatePolicy AsPolicy(string policy) => policy switch 20 | { 21 | "BLOCK" => TsDuplicatePolicy.BLOCK, 22 | "FIRST" => TsDuplicatePolicy.FIRST, 23 | "LAST" => TsDuplicatePolicy.LAST, 24 | "MIN" => TsDuplicatePolicy.MIN, 25 | "MAX" => TsDuplicatePolicy.MAX, 26 | "SUM" => TsDuplicatePolicy.SUM, 27 | _ => throw new ArgumentOutOfRangeException(nameof(policy), $"Invalid policy type '{policy}'"), 28 | }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /NRedisTimeSeries/Commands/Enums/DuplicatePolicy.cs: -------------------------------------------------------------------------------- 1 | namespace NRedisTimeSeries.Commands.Enums 2 | { 3 | /// 4 | /// Policy to handle duplicate samples. 5 | /// The default policy for database-wide is BLOCK. 6 | /// 7 | public enum TsDuplicatePolicy 8 | { 9 | /// 10 | /// An error will occur for any out of order sample. 11 | /// 12 | BLOCK, 13 | 14 | /// 15 | /// Ignore the new value. 16 | /// 17 | FIRST, 18 | 19 | /// 20 | /// Override with latest value. 21 | /// 22 | LAST, 23 | 24 | /// 25 | /// Only override if the value is lower than the existing value. 26 | /// 27 | MIN, 28 | 29 | /// 30 | /// Only override if the value is higher than the existing value. 31 | /// 32 | MAX, 33 | 34 | /// 35 | /// If a previous sample exists, add the new sample to it so that the updated value is equal to (previous + new). 36 | /// If no previous sample exists, set the updated value equal to the new value. 37 | /// 38 | SUM 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/AlterExampleAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using StackExchange.Redis; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace NRedisTimeSeries.Example 7 | { 8 | /// 9 | /// Examples for NRedisTimeSeries async API for altering time series properties. 10 | /// 11 | internal class AlterAsyncExample 12 | { 13 | /// 14 | /// Examples for altering time-series. 15 | /// The parameters retentionTime, and labels are optional and can be set in any order when used as named argument. 16 | /// 17 | public static async Task ParameterizedAlterAsync() 18 | { 19 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 20 | IDatabase db = redis.GetDatabase(); 21 | var label = new TimeSeriesLabel("key", "value"); 22 | var labels = new List { label }; 23 | await db.TimeSeriesAlterAsync("labeld_ts", labels: labels); 24 | await db.TimeSeriesAlterAsync("retention_time_ts", retentionTime: 5000); 25 | await db.TimeSeriesAlterAsync("parameterized_ts", retentionTime: 5000, labels: labels); 26 | redis.Close(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestDataTypes/TestTimeSeriesInformation.cs: -------------------------------------------------------------------------------- 1 | using StackExchange.Redis; 2 | using NRedisTimeSeries.DataTypes; 3 | using NRedisTimeSeries.Commands.Enums; 4 | using NRedisTimeSeries.Test.TestAPI; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | 8 | namespace NRedisTimeSeries.Test.TestDataTypes 9 | { 10 | public class TestInformation : AbstractTimeSeriesTest 11 | { 12 | public TestInformation(RedisFixture redisFixture) : base(redisFixture) { } 13 | 14 | [Fact] 15 | public async Task TestInformationToStringAsync() 16 | { 17 | string key = CreateKeyName(); 18 | IDatabase db = redisFixture.Redis.GetDatabase(); 19 | await db.TimeSeriesAddAsync(key, "*", 1.1); 20 | await db.TimeSeriesAddAsync(key, "*", 1.3, duplicatePolicy: TsDuplicatePolicy.LAST); 21 | TimeSeriesInformation info = await db.TimeSeriesInfoAsync(key); 22 | string[] infoProperties = ((string)info).Trim('{').Trim('}').Split(","); 23 | Assert.Equal("\"TotalSamples\":2", infoProperties[0]); 24 | Assert.Equal("\"MemoryUsage\":4184", infoProperties[1]); 25 | Assert.Equal("\"RetentionTime\":0", infoProperties[4]); 26 | Assert.Equal("\"ChunkCount\":1", infoProperties[5]); 27 | Assert.Equal("\"DuplicatePolicy\":null", infoProperties[11]); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/MAddExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.DataTypes; 4 | using StackExchange.Redis; 5 | 6 | namespace NRedisTimeSeries.Example 7 | { 8 | /// 9 | /// Examples for NRedisTimeSeries API for adding multiple samples to multiple time series. 10 | /// 11 | internal class MAddExample 12 | { 13 | /// 14 | /// Example for mutiple sample addtion. One is using RedisTimeSeris default system time in one time series, 15 | /// the second is using DateTime in the second time series and the third is using long in the third time series. 16 | /// 17 | public static void MAddFlowExample() 18 | { 19 | string[] keys = { "system_time_ts", "datetime_ts", "long_ts" }; 20 | var sequence = new List<(string, TimeStamp, double)>(keys.Length); 21 | // Add sample to the system_time_ts 22 | sequence.Add((keys[0], "*", 0.0)); 23 | // Add sample to the datetime_ts 24 | sequence.Add((keys[1], DateTime.UtcNow, 0.0)); 25 | // Add sample to the long_ts 26 | sequence.Add((keys[2], 1, 0.0)); 27 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 28 | IDatabase db = redis.GetDatabase(); 29 | db.TimeSeriesMAdd(sequence); 30 | redis.Close(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestQueryIndex.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.DataTypes; 4 | using Xunit; 5 | 6 | namespace NRedisTimeSeries.Test.TestAPI 7 | { 8 | public class TestQueryIndex : AbstractTimeSeriesTest, IDisposable 9 | { 10 | private readonly string[] keys = { "QUERYINDEX_TESTS_1", "QUERYINDEX_TESTS_2" }; 11 | 12 | public TestQueryIndex(RedisFixture redisFixture) : base(redisFixture) { } 13 | 14 | public void Dispose() 15 | { 16 | foreach (var key in keys) 17 | { 18 | redisFixture.Redis.GetDatabase().KeyDelete(key); 19 | } 20 | } 21 | 22 | [Fact] 23 | public void TestTSQueryIndex() 24 | { 25 | var db = redisFixture.Redis.GetDatabase(); 26 | var label1 = new TimeSeriesLabel("QUERYINDEX_TESTS_1", "value"); 27 | var label2 = new TimeSeriesLabel("QUERYINDEX_TESTS_2", "value2"); 28 | var labels1 = new List { label1, label2 }; 29 | var labels2 = new List { label1 }; 30 | 31 | db.TimeSeriesCreate(keys[0], labels: labels1); 32 | db.TimeSeriesCreate(keys[1], labels: labels2); 33 | Assert.Equal(keys, db.TimeSeriesQueryIndex(new List { "QUERYINDEX_TESTS_1=value" })); 34 | Assert.Equal(new List { keys[0] }, db.TimeSeriesQueryIndex(new List { "QUERYINDEX_TESTS_2=value2" })); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/MAddExampleAsync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using NRedisTimeSeries.DataTypes; 5 | using StackExchange.Redis; 6 | 7 | namespace NRedisTimeSeries.Example 8 | { 9 | /// 10 | /// Examples for NRedisTimeSeries async API for adding multiple samples to multiple time series. 11 | /// 12 | internal class MAddAsyncExample 13 | { 14 | /// 15 | /// Example for mutiple sample addtion. One is using RedisTimeSeris default system time in one time series, 16 | /// the second is using DateTime in the second time series and the third is using long in the third time series. 17 | /// 18 | public static async Task MAddFlowAsyncExample() 19 | { 20 | string[] keys = { "system_time_ts", "datetime_ts", "long_ts" }; 21 | var sequence = new List<(string, TimeStamp, double)>(keys.Length); 22 | // Add sample to the system_time_ts 23 | sequence.Add((keys[0], "*", 0.0)); 24 | // Add sample to the datetime_ts 25 | sequence.Add((keys[1], DateTime.UtcNow, 0.0)); 26 | // Add sample to the long_ts 27 | sequence.Add((keys[2], 1, 0.0)); 28 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 29 | IDatabase db = redis.GetDatabase(); 30 | await db.TimeSeriesMAddAsync(sequence); 31 | redis.Close(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestDataTypes/TestTimeSeriesTuple.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NRedisTimeSeries.DataTypes; 3 | using Xunit; 4 | 5 | namespace NRedisTimeSeries.Test 6 | { 7 | public class TestTimeSeriesTuple 8 | { 9 | [Fact] 10 | public void TestTupleConstructor() 11 | { 12 | TimeSeriesTuple tuple = new TimeSeriesTuple(1, 1.1); 13 | Assert.Equal(1, tuple.Time); 14 | Assert.Equal(1.1, tuple.Val); 15 | } 16 | 17 | [Fact] 18 | public void TestTupleEqual() 19 | { 20 | TimeSeriesTuple tuple1 = new TimeSeriesTuple(1, 1.1); 21 | TimeSeriesTuple tuple1_1 = new TimeSeriesTuple(1, 1.1); 22 | TimeSeriesTuple tuple1_2 = new TimeSeriesTuple(2, 2.2); 23 | Assert.Equal(tuple1, tuple1_1); 24 | Assert.NotEqual(tuple1, tuple1_2); 25 | } 26 | 27 | [Fact] 28 | public void TestTupleHashCode() 29 | { 30 | TimeSeriesTuple tuple1 = new TimeSeriesTuple(1, 1.1); 31 | TimeSeriesTuple tuple1_1 = new TimeSeriesTuple(1, 1.1); 32 | TimeSeriesTuple tuple1_2 = new TimeSeriesTuple(2, 2.2); 33 | Assert.Equal(tuple1.GetHashCode(), tuple1_1.GetHashCode()); 34 | Assert.NotEqual(tuple1.GetHashCode(), tuple1_2.GetHashCode()); 35 | } 36 | 37 | [Fact] 38 | public void TestTupleToString() 39 | { 40 | TimeSeriesTuple tuple = new TimeSeriesTuple(1, 1.1); 41 | Assert.Equal("Time: 1, Val:1.1", (string)tuple); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, RedisTimeSeries 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestGet.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using StackExchange.Redis; 3 | using System; 4 | using Xunit; 5 | 6 | namespace NRedisTimeSeries.Test.TestAPI 7 | { 8 | public class TestGet : AbstractTimeSeriesTest, IDisposable 9 | { 10 | 11 | private readonly string key = "GET_TESTS"; 12 | 13 | public TestGet(RedisFixture redisFixture) : base(redisFixture) { } 14 | 15 | public void Dispose() 16 | { 17 | redisFixture.Redis.GetDatabase().KeyDelete(key); 18 | } 19 | 20 | [Fact] 21 | public void TestGetNotExists() 22 | { 23 | IDatabase db = redisFixture.Redis.GetDatabase(); 24 | var ex = Assert.Throws(()=>db.TimeSeriesGet(key)); 25 | Assert.Equal("ERR TSDB: the key does not exist", ex.Message); 26 | } 27 | 28 | [Fact] 29 | public void TestEmptyGet() 30 | { 31 | IDatabase db = redisFixture.Redis.GetDatabase(); 32 | db.TimeSeriesCreate(key); 33 | Assert.Null(db.TimeSeriesGet(key)); 34 | } 35 | 36 | [Fact] 37 | public void TestAddAndGet() 38 | { 39 | DateTime now = DateTime.UtcNow; 40 | TimeSeriesTuple expected = new TimeSeriesTuple(now, 1.1); 41 | IDatabase db = redisFixture.Redis.GetDatabase(); 42 | db.TimeSeriesCreate(key); 43 | db.TimeSeriesAdd(key, now, 1.1); 44 | TimeSeriesTuple actual = db.TimeSeriesGet(key); 45 | Assert.Equal(expected, actual); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestGetAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using StackExchange.Redis; 3 | using System; 4 | using System.Threading.Tasks; 5 | using Xunit; 6 | 7 | namespace NRedisTimeSeries.Test.TestAPI 8 | { 9 | public class TestGetAsync : AbstractTimeSeriesTest 10 | { 11 | public TestGetAsync(RedisFixture redisFixture) : base(redisFixture) { } 12 | 13 | [Fact] 14 | public async Task TestGetNotExists() 15 | { 16 | var key = CreateKeyName(); 17 | var db = redisFixture.Redis.GetDatabase(); 18 | var ex = await Assert.ThrowsAsync(async () => await db.TimeSeriesGetAsync(key)); 19 | Assert.Equal("ERR TSDB: the key does not exist", ex.Message); 20 | } 21 | 22 | [Fact] 23 | public async Task TestEmptyGet() 24 | { 25 | var key = CreateKeyName(); 26 | var db = redisFixture.Redis.GetDatabase(); 27 | await db.TimeSeriesCreateAsync(key); 28 | Assert.Null(await db.TimeSeriesGetAsync(key)); 29 | } 30 | 31 | [Fact] 32 | public async Task TestAddAndGet() 33 | { 34 | var key = CreateKeyName(); 35 | var now = DateTime.UtcNow; 36 | var expected = new TimeSeriesTuple(now, 1.1); 37 | var db = redisFixture.Redis.GetDatabase(); 38 | await db.TimeSeriesCreateAsync(key); 39 | await db.TimeSeriesAddAsync(key, now, 1.1); 40 | var actual = await db.TimeSeriesGetAsync(key); 41 | Assert.Equal(expected, actual); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/InfoQueryIndexExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.DataTypes; 4 | using StackExchange.Redis; 5 | 6 | namespace NRedisTimeSeries.Example 7 | { 8 | /// 9 | /// Examples for NRedisTimeSeries API for INFO and QUERYINDEX commands. 10 | /// 11 | internal class InfoQueryIndexExample 12 | { 13 | /// 14 | /// Example for getting the information of a timeseries key with INFO command. 15 | /// 16 | public static void InfoExample() 17 | { 18 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 19 | IDatabase db = redis.GetDatabase(); 20 | TimeSeriesInformation info = db.TimeSeriesInfo("my_ts"); 21 | // Now you can access to all the properties of TimeSeriesInformation 22 | Console.WriteLine(info.TotalSamples); 23 | Console.WriteLine((string)info.FirstTimeStamp); 24 | Console.WriteLine((string)info.LastTimeStamp); 25 | redis.Close(); 26 | } 27 | 28 | /// 29 | /// Example for using QUERYINDEX. 30 | /// 31 | public static void QueryIndexExample() 32 | { 33 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 34 | IDatabase db = redis.GetDatabase(); 35 | var filter = new List { "key=value" }; 36 | IReadOnlyList keys = db.TimeSeriesQueryIndex(filter); 37 | foreach(var key in keys) { 38 | Console.WriteLine(key); 39 | } 40 | redis.Close(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestAlterAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Xunit; 5 | 6 | namespace NRedisTimeSeries.Test.TestAPI 7 | { 8 | public class TestAlterAsync : AbstractTimeSeriesTest 9 | { 10 | public TestAlterAsync(RedisFixture redisFixture) : base(redisFixture) { } 11 | 12 | [Fact] 13 | public async Task TestAlterRetentionTime() 14 | { 15 | var key = CreateKeyName(); 16 | long retentionTime = 5000; 17 | var db = redisFixture.Redis.GetDatabase(); 18 | await db.TimeSeriesCreateAsync(key); 19 | Assert.True(await db.TimeSeriesAlterAsync(key, retentionTime: retentionTime)); 20 | 21 | var info = await db.TimeSeriesInfoAsync(key); 22 | Assert.Equal(retentionTime, info.RetentionTime); 23 | } 24 | 25 | [Fact] 26 | public async Task TestAlterLabels() 27 | { 28 | var key = CreateKeyName(); 29 | var db = redisFixture.Redis.GetDatabase(); 30 | var label = new TimeSeriesLabel("key", "value"); 31 | var labels = new List { label }; 32 | await db.TimeSeriesCreateAsync(key); 33 | Assert.True(await db.TimeSeriesAlterAsync(key, labels: labels)); 34 | 35 | var info = await db.TimeSeriesInfoAsync(key); 36 | Assert.Equal(labels, info.Labels); 37 | 38 | labels.Clear(); 39 | Assert.True(await db.TimeSeriesAlterAsync(key, labels: labels)); 40 | 41 | info = await db.TimeSeriesInfoAsync(key); 42 | Assert.Equal(labels, info.Labels); 43 | } 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestAlter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.DataTypes; 4 | using StackExchange.Redis; 5 | using Xunit; 6 | 7 | namespace NRedisTimeSeries.Test.TestAPI 8 | { 9 | public class TestAlter : AbstractTimeSeriesTest, IDisposable 10 | { 11 | private readonly string key = "ALTER_TESTS"; 12 | 13 | public TestAlter(RedisFixture redisFixture): base(redisFixture) { } 14 | 15 | public void Dispose() 16 | { 17 | redisFixture.Redis.GetDatabase().KeyDelete(key); 18 | } 19 | 20 | [Fact] 21 | public void TestAlterRetentionTime() 22 | { 23 | long retentionTime = 5000; 24 | IDatabase db = redisFixture.Redis.GetDatabase(); 25 | db.TimeSeriesCreate(key); 26 | Assert.True(db.TimeSeriesAlter(key, retentionTime: retentionTime)); 27 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 28 | Assert.Equal(retentionTime, info.RetentionTime); 29 | } 30 | 31 | [Fact] 32 | public void TestAlterLabels() 33 | { 34 | TimeSeriesLabel label = new TimeSeriesLabel("key", "value"); 35 | var labels = new List { label }; 36 | IDatabase db = redisFixture.Redis.GetDatabase(); 37 | db.TimeSeriesCreate(key); 38 | Assert.True(db.TimeSeriesAlter(key, labels: labels)); 39 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 40 | Assert.Equal(labels, info.Labels); 41 | labels.Clear(); 42 | Assert.True(db.TimeSeriesAlter(key, labels: labels)); 43 | info = db.TimeSeriesInfo(key); 44 | Assert.Equal(labels, info.Labels); 45 | } 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /NRedisTimeSeries/Extensions/AggregationExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NRedisTimeSeries.Commands.Enums; 3 | 4 | namespace NRedisTimeSeries.Extensions 5 | { 6 | internal static class AggregationExtensions 7 | { 8 | public static string AsArg(this TsAggregation aggregation) => aggregation switch 9 | { 10 | TsAggregation.Avg => "AVG", 11 | TsAggregation.Sum => "SUM", 12 | TsAggregation.Min => "MIN", 13 | TsAggregation.Max => "MAX", 14 | TsAggregation.Range => "RANGE", 15 | TsAggregation.Count => "COUNT", 16 | TsAggregation.First => "FIRST", 17 | TsAggregation.Last => "LAST", 18 | TsAggregation.StdP => "STD.P", 19 | TsAggregation.StdS => "STD.S", 20 | TsAggregation.VarP => "VAR.P", 21 | TsAggregation.VarS => "VAR.S", 22 | _ => throw new ArgumentOutOfRangeException(nameof(aggregation), "Invalid aggregation type"), 23 | }; 24 | 25 | public static TsAggregation AsAggregation(string aggregation) => aggregation switch 26 | { 27 | "AVG" => TsAggregation.Avg, 28 | "SUM" => TsAggregation.Sum, 29 | "MIN" => TsAggregation.Min, 30 | "MAX" => TsAggregation.Max, 31 | "RANGE" => TsAggregation.Range, 32 | "COUNT" => TsAggregation.Count, 33 | "FIRST" => TsAggregation.First, 34 | "LAST" => TsAggregation.Last, 35 | "STD.P" => TsAggregation.StdP, 36 | "STD.S" => TsAggregation.StdS, 37 | "VAR.P" => TsAggregation.VarP, 38 | "VAR.S" => TsAggregation.VarS, 39 | _ => throw new ArgumentOutOfRangeException(nameof(aggregation), $"Invalid aggregation type '{aggregation}'"), 40 | }; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/CreateExample.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using NRedisTimeSeries.Commands.Enums; 3 | using StackExchange.Redis; 4 | using System.Collections.Generic; 5 | 6 | namespace NRedisTimeSeries.Example 7 | { 8 | /// 9 | /// Examples for NRedisTimeSeries API for creating new time series. 10 | /// 11 | internal class CreateExample 12 | { 13 | /// 14 | /// Simple time-series creation. 15 | /// 16 | public static void SimpleCreate() 17 | { 18 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 19 | IDatabase db = redis.GetDatabase(); 20 | db.TimeSeriesCreate("my_ts"); 21 | redis.Close(); 22 | } 23 | 24 | /// 25 | /// Examples for creating time-series with parameters. 26 | /// The parameters retentionTime, uncompressed and labels are optional and can be set in any order when used as named argument. 27 | /// 28 | public static void ParameterizedCreate() 29 | { 30 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 31 | IDatabase db = redis.GetDatabase(); 32 | db.TimeSeriesCreate("retentionTime_ts", retentionTime: 5000); 33 | db.TimeSeriesCreate("uncompressed_ts", uncompressed: true); 34 | var label = new TimeSeriesLabel("key", "value"); 35 | var labels = new List { label }; 36 | db.TimeSeriesCreate("labeled_ts", labels: labels); 37 | db.TimeSeriesCreate("parameterized_ts", labels: labels, uncompressed: true, retentionTime: 5000, duplicatePolicy: TsDuplicatePolicy.LAST); 38 | redis.Close(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/InfoQueryIndexExampleAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using StackExchange.Redis; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace NRedisTimeSeries.Example 8 | { 9 | /// 10 | /// Examples for NRedisTimeSeries async API for INFO and QUERYINDEX commands. 11 | /// 12 | internal class InfoQueryIndexAsyncExample 13 | { 14 | /// 15 | /// Example for getting the information of a timeseries key with INFO command. 16 | /// 17 | public static async Task InfoAsyncExample() 18 | { 19 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 20 | IDatabase db = redis.GetDatabase(); 21 | TimeSeriesInformation info = await db.TimeSeriesInfoAsync("my_ts"); 22 | // Now you can access to all the properties of TimeSeriesInformation 23 | Console.WriteLine(info.TotalSamples); 24 | Console.WriteLine((string)info.FirstTimeStamp); 25 | Console.WriteLine((string)info.LastTimeStamp); 26 | redis.Close(); 27 | } 28 | 29 | /// 30 | /// Example for using QUERYINDEX. 31 | /// 32 | public static async Task QueryIndexAsyncExample() 33 | { 34 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 35 | IDatabase db = redis.GetDatabase(); 36 | var filter = new List { "key=value" }; 37 | IReadOnlyList keys = await db.TimeSeriesQueryIndexAsync(filter); 38 | foreach(var key in keys) { 39 | Console.WriteLine(key); 40 | } 41 | redis.Close(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestDataTypes/TestTimeSeriesLabel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NRedisTimeSeries.DataTypes; 3 | using Xunit; 4 | 5 | namespace NRedisTimeSeries.Test.TestDataTypes 6 | { 7 | public class TestLabel 8 | { 9 | [Fact] 10 | public void TestLabelConstructor() 11 | { 12 | TimeSeriesLabel label = new TimeSeriesLabel("a", "b"); 13 | Assert.Equal("a", label.Key); 14 | Assert.Equal("b", label.Value); 15 | } 16 | 17 | 18 | [Fact] 19 | public void TestLbaelEquals() 20 | { 21 | TimeSeriesLabel label_ab = new TimeSeriesLabel("a", "b"); 22 | TimeSeriesLabel label1 = new TimeSeriesLabel("a", "b"); 23 | TimeSeriesLabel label2 = new TimeSeriesLabel("a", "c"); 24 | TimeSeriesLabel label3 = new TimeSeriesLabel("c", "b"); 25 | 26 | Assert.Equal(label_ab, label1); 27 | Assert.NotEqual(label_ab, label2); 28 | Assert.NotEqual(label_ab, label3); 29 | } 30 | 31 | [Fact] 32 | public void TestLabelHashCode() 33 | { 34 | TimeSeriesLabel label_ab = new TimeSeriesLabel("a", "b"); 35 | TimeSeriesLabel label1 = new TimeSeriesLabel("a", "b"); 36 | TimeSeriesLabel label2 = new TimeSeriesLabel("a", "c"); 37 | TimeSeriesLabel label3 = new TimeSeriesLabel("c", "b"); 38 | 39 | Assert.Equal(label_ab.GetHashCode(), label1.GetHashCode()); 40 | Assert.NotEqual(label_ab.GetHashCode(), label2.GetHashCode()); 41 | Assert.NotEqual(label_ab.GetHashCode(), label3.GetHashCode()); 42 | } 43 | 44 | [Fact] 45 | public void TestLabelToString() 46 | { 47 | TimeSeriesLabel label = new TimeSeriesLabel("a", "b"); 48 | Assert.Equal("Key: a, Val:b", (string)label); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/CreateExampleAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using NRedisTimeSeries.Commands.Enums; 3 | using StackExchange.Redis; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace NRedisTimeSeries.Example 8 | { 9 | /// 10 | /// Examples for NRedisTimeSeries async API for creating new time series. 11 | /// 12 | internal class CreateAsyncExample 13 | { 14 | /// 15 | /// Simple time-series creation. 16 | /// 17 | public static async Task SimpleCreateAsync() 18 | { 19 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 20 | IDatabase db = redis.GetDatabase(); 21 | await db.TimeSeriesCreateAsync("my_ts"); 22 | redis.Close(); 23 | } 24 | 25 | /// 26 | /// Examples for creating time-series with parameters. 27 | /// The parameters retentionTime, uncompressed and labels are optional and can be set in any order when used as named argument. 28 | /// 29 | public static async Task ParameterizedCreateAsync() 30 | { 31 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 32 | IDatabase db = redis.GetDatabase(); 33 | await db.TimeSeriesCreateAsync("retentionTime_ts", retentionTime: 5000); 34 | await db.TimeSeriesCreateAsync("uncompressed_ts", uncompressed: true); 35 | var label = new TimeSeriesLabel("key", "value"); 36 | var labels = new List { label }; 37 | await db.TimeSeriesCreateAsync("labeled_ts", labels: labels); 38 | await db.TimeSeriesCreateAsync("parameterized_ts", labels: labels, uncompressed: true, retentionTime: 5000, duplicatePolicy: TsDuplicatePolicy.LAST); 39 | redis.Close(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestDel.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using StackExchange.Redis; 3 | using System; 4 | using System.Collections.Generic; 5 | using Xunit; 6 | 7 | 8 | namespace NRedisTimeSeries.Test.TestAPI 9 | { 10 | public class TestDel : AbstractTimeSeriesTest, IDisposable 11 | { 12 | private readonly string key = "DEL_TESTS"; 13 | 14 | public TestDel(RedisFixture redisFixture) : base(redisFixture) { } 15 | 16 | public void Dispose() 17 | { 18 | redisFixture.Redis.GetDatabase().KeyDelete(key); 19 | } 20 | 21 | private List CreateData(IDatabase db, int timeBucket) 22 | { 23 | var tuples = new List(); 24 | for (int i = 0; i < 10; i++) 25 | { 26 | TimeStamp ts = db.TimeSeriesAdd(key, i*timeBucket, i); 27 | tuples.Add(new TimeSeriesTuple(ts, i)); 28 | } 29 | return tuples; 30 | } 31 | 32 | [Fact] 33 | public void TestDelNotExists() 34 | { 35 | IDatabase db = redisFixture.Redis.GetDatabase(); 36 | var ex = Assert.Throws(() => db.TimeSeriesDel(key, "-", "+")); 37 | Assert.Equal("ERR TSDB: the key does not exist", ex.Message); 38 | } 39 | 40 | [Fact] 41 | public void TestDelRange() 42 | { 43 | IDatabase db = redisFixture.Redis.GetDatabase(); 44 | var tuples = CreateData(db, 50); 45 | TimeStamp from = tuples[0].Time; 46 | TimeStamp to = tuples[5].Time; 47 | Assert.Equal(6, db.TimeSeriesDel(key, from, to)); 48 | 49 | // check that the operation deleted the timestamps 50 | IReadOnlyList res = db.TimeSeriesRange(key, from, to); 51 | Assert.Equal(0, res.Count); 52 | Assert.NotNull(db.TimeSeriesGet(key)); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestDelAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using StackExchange.Redis; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using Xunit; 6 | 7 | namespace NRedisTimeSeries.Test.TestAPI 8 | { 9 | public class TestDelAsync : AbstractTimeSeriesTest 10 | { 11 | public TestDelAsync(RedisFixture redisFixture) : base(redisFixture) { } 12 | 13 | private async Task> CreateData(IDatabase db, string key, int timeBucket) 14 | { 15 | var tuples = new List(); 16 | for (var i = 0; i < 10; i++) 17 | { 18 | var ts = await db.TimeSeriesAddAsync(key, i * timeBucket, i); 19 | tuples.Add(new TimeSeriesTuple(ts, i)); 20 | } 21 | return tuples; 22 | } 23 | 24 | [Fact] 25 | public async Task TestDelNotExists() 26 | { 27 | var key = CreateKeyName(); 28 | IDatabase db = redisFixture.Redis.GetDatabase(); 29 | var ex = await Assert.ThrowsAsync(async () => await db.TimeSeriesDelAsync(key, "-", "+")); 30 | Assert.Equal("ERR TSDB: the key does not exist", ex.Message); 31 | } 32 | 33 | [Fact] 34 | public async Task TestDelRange() 35 | { 36 | IDatabase db = redisFixture.Redis.GetDatabase(); 37 | var key = CreateKeyName(); 38 | var tuples = await CreateData(db, key, 50); 39 | TimeStamp from = tuples[0].Time; 40 | TimeStamp to = tuples[5].Time; 41 | Assert.Equal(6, await db.TimeSeriesDelAsync(key, from, to)); 42 | 43 | // check that the operation deleted the timestamps 44 | IReadOnlyList res = await db.TimeSeriesRangeAsync(key, from, to); 45 | Assert.Equal(0, res.Count); 46 | Assert.NotNull(await db.TimeSeriesGetAsync(key)); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/AbstractTimeSeriesTest.cs: -------------------------------------------------------------------------------- 1 | using StackExchange.Redis; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Runtime.CompilerServices; 6 | using System.Threading.Tasks; 7 | using NRedisTimeSeries.DataTypes; 8 | using Xunit; 9 | 10 | namespace NRedisTimeSeries.Test.TestAPI 11 | { 12 | public abstract class AbstractTimeSeriesTest : IClassFixture, IAsyncLifetime 13 | { 14 | protected internal RedisFixture redisFixture; 15 | 16 | protected internal AbstractTimeSeriesTest(RedisFixture redisFixture) => this.redisFixture = redisFixture; 17 | 18 | private List keyNames = new List(); 19 | 20 | protected internal string CreateKeyName([CallerMemberName] string memberName = "") => CreateKeyNames(1, memberName)[0]; 21 | 22 | protected internal string[] CreateKeyNames(int count, [CallerMemberName] string memberName = "") 23 | { 24 | if (count < 1) throw new ArgumentOutOfRangeException(nameof(count), "Must be greater than zero."); 25 | 26 | var newKeys = new string[count]; 27 | for (var i = 0; i < count; i++) 28 | { 29 | newKeys[i] = $"{GetType().Name}:{memberName}:{i}"; 30 | } 31 | 32 | keyNames.AddRange(newKeys); 33 | 34 | return newKeys; 35 | } 36 | 37 | protected internal static List ReverseData(List data) 38 | { 39 | var tuples = new List(data.Count); 40 | for (var i = data.Count - 1; i >= 0; i--) 41 | { 42 | tuples.Add(data[i]); 43 | } 44 | 45 | return tuples; 46 | } 47 | 48 | public Task InitializeAsync() => Task.CompletedTask; 49 | 50 | public async Task DisposeAsync() 51 | { 52 | var redis = redisFixture.Redis.GetDatabase(); 53 | await redis.KeyDeleteAsync(keyNames.Select(i => (RedisKey)i).ToArray()); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /NRedisTimeSeries/DataTypes/TimeSeriesLabel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace NRedisTimeSeries.DataTypes 4 | { 5 | /// 6 | /// A key-value pair class represetns metadata label of time-series. 7 | /// 8 | public class TimeSeriesLabel 9 | { 10 | /// 11 | /// Label key 12 | /// 13 | public string Key { get; } 14 | 15 | /// 16 | /// Label value 17 | /// 18 | public string Value { get; } 19 | 20 | /// 21 | /// Create a new TimeSeriesLabel out of key and value strings. 22 | /// 23 | /// Key string 24 | /// Value string 25 | public TimeSeriesLabel(string key, string value) => (Key, Value) = (key, value); 26 | 27 | /// 28 | /// Equality of TimeSeriesLabel objects 29 | /// 30 | /// Object to compare 31 | /// If two TimeSeriesLabel objects are equal 32 | public override bool Equals(object obj) => 33 | obj is TimeSeriesLabel label && 34 | Key == label.Key && 35 | Value == label.Value; 36 | 37 | /// 38 | /// Implicit cast from TimeSeriesLabel to string. 39 | /// 40 | /// TimeSeriesLabel 41 | public static implicit operator string(TimeSeriesLabel tsl) => string.Format("Key: {0}, Val:{1}", tsl.Key, tsl.Value); 42 | 43 | /// 44 | /// TimeSeriesLabel object hash code. 45 | /// 46 | /// TimeSeriesLabel object hash code. 47 | public override int GetHashCode() 48 | { 49 | var hashCode = 206514262; 50 | hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(Key); 51 | hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(Value); 52 | return hashCode; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /NRedisTimeSeries/DataTypes/TimeSeriesTuple.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace NRedisTimeSeries.DataTypes 4 | { 5 | /// 6 | /// A class represents time-series timestamp-value pair 7 | /// 8 | public class TimeSeriesTuple 9 | { 10 | /// 11 | /// Tuple key - timestamp. 12 | /// 13 | public TimeStamp Time { get; } 14 | 15 | /// 16 | /// Tuple value 17 | /// 18 | public double Val { get; } 19 | 20 | /// 21 | /// Create new TimeSeriesTuple. 22 | /// 23 | /// Timestamp 24 | /// Value 25 | public TimeSeriesTuple(TimeStamp time, double val) => (Time, Val) = (time, val); 26 | 27 | /// 28 | /// Equality of TimeSeriesTuple objects 29 | /// 30 | /// Object to compare 31 | /// If two TimeSeriesTuple objects are equal 32 | public override bool Equals(object obj) => 33 | obj is TimeSeriesTuple tuple && 34 | EqualityComparer.Default.Equals(Time, tuple.Time) && 35 | Val == tuple.Val; 36 | 37 | /// 38 | /// Implicit cast from TimeSeriesTuple to string. 39 | /// 40 | /// TimeSeriesTuple 41 | public static implicit operator string(TimeSeriesTuple tst) => 42 | string.Format("Time: {0}, Val:{1}", (string)tst.Time, tst.Val); 43 | 44 | /// 45 | /// TimeSeriesTuple object hash code. 46 | /// 47 | /// TimeSeriesTuple object hash code. 48 | public override int GetHashCode() 49 | { 50 | var hashCode = 459537088; 51 | hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(Time); 52 | hashCode = (hashCode * -1521134295) + Val.GetHashCode(); 53 | return hashCode; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /NRedisTimeSeries/Commands/Enums/Aggregation.cs: -------------------------------------------------------------------------------- 1 | namespace NRedisTimeSeries.Commands.Enums 2 | { 3 | /// 4 | /// An aggregation type to be used with a time bucket. 5 | /// 6 | public enum TsAggregation 7 | { 8 | /// 9 | /// The average of all samples in the aggregation 10 | /// 11 | Avg, 12 | 13 | /// 14 | /// A sum of all samples in the aggregation 15 | /// 16 | Sum, 17 | 18 | /// 19 | /// A minimum sample of all samples in the aggregation 20 | /// 21 | Min, 22 | 23 | /// 24 | /// A maximum sample of all samples in the aggregation 25 | /// 26 | Max, 27 | 28 | /// 29 | /// A range of the min and max sample of all samples in the aggregation (range r = max-min) 30 | /// For example if the min sample was 100 and the max was 400, the range aggregation would return 300 31 | /// 32 | Range, 33 | 34 | /// 35 | /// The total number of all samples in the aggregation 36 | /// 37 | Count, 38 | 39 | /// 40 | /// The first sample in the aggregation 41 | /// 42 | First, 43 | 44 | /// 45 | /// The last sample in the aggregation 46 | /// 47 | Last, 48 | 49 | /// 50 | /// The standard deviation based on the entire population 51 | /// The standard deviation is a measure of how widely values are dispersed from the average sample in the aggregation 52 | /// 53 | StdP, 54 | 55 | /// 56 | /// The standard deviation based on a sample of the population 57 | /// The standard deviation is a measure of how widely values are dispersed from the average sample in the aggregation 58 | /// 59 | StdS, 60 | 61 | /// 62 | /// The variance based on the entire population 63 | /// The variance is the average of the squared differences from the mean 64 | /// 65 | VarP, 66 | 67 | /// 68 | /// The variance based on a sample of the population 69 | /// The variance is the average of the squared differences from the mean 70 | /// 71 | VarS, 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/MGetExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.DataTypes; 4 | using StackExchange.Redis; 5 | 6 | namespace NRedisTimeSeries.Example 7 | { 8 | /// 9 | /// Examples for NRedisTimeSeries API for MGET queries. 10 | /// 11 | internal class MGetExample 12 | { 13 | /// 14 | /// Example for basic usage of RedisTimeSeries MGET command with a filter. 15 | /// The NRedisTimeSeries SimpleMGetExample returns and IReadOnlyList<(string key, IReadOnlyList labels, TimeSeriesTuple value)> collection. 16 | /// 17 | public static void SimpleMGetExample() 18 | { 19 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 20 | IDatabase db = redis.GetDatabase(); 21 | var filter = new List { "key=value" }; 22 | var results = db.TimeSeriesMGet(filter); 23 | // Values extraction example. No lables in this case. 24 | foreach (var result in results) 25 | { 26 | Console.WriteLine(result.key); 27 | TimeSeriesTuple value = result.value; 28 | Console.WriteLine(value); 29 | } 30 | redis.Close(); 31 | } 32 | 33 | /// 34 | /// Example for basic usage of RedisTimeSeries MGET command with a filter and WITHLABELS flag. 35 | /// The NRedisTimeSeries SimpleMGetExample returns and IReadOnlyList<(string key, IReadOnlyList labels, TimeSeriesTuple value)> collection. 36 | /// 37 | public static void MGetWithLabelsExample() 38 | { 39 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 40 | IDatabase db = redis.GetDatabase(); 41 | var filter = new List { "key=value" }; 42 | var results = db.TimeSeriesMGet(filter, withLabels: true); 43 | // Values extraction example. 44 | foreach (var result in results) 45 | { 46 | Console.WriteLine(result.key); 47 | IReadOnlyList labels = result.labels; 48 | foreach(TimeSeriesLabel label in labels){ 49 | Console.WriteLine(label); 50 | } 51 | TimeSeriesTuple value = result.value; 52 | Console.WriteLine(value); 53 | } 54 | redis.Close(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestDataTypes/TestTimeSeriesRule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NRedisTimeSeries.Commands; 3 | using NRedisTimeSeries.Commands.Enums; 4 | using NRedisTimeSeries.DataTypes; 5 | using Xunit; 6 | 7 | namespace NRedisTimeSeries.Test.TestDataTypes 8 | { 9 | public class TestTimeSeriesRule 10 | { 11 | public TestTimeSeriesRule() { } 12 | 13 | [Fact] 14 | public void TestRuleConstructor() 15 | { 16 | TimeSeriesRule rule = new TimeSeriesRule("key", 50, TsAggregation.Avg); 17 | Assert.Equal("key", rule.DestKey); 18 | Assert.Equal(TsAggregation.Avg, rule.Aggregation); 19 | Assert.Equal(50, rule.TimeBucket); 20 | } 21 | 22 | [Fact] 23 | public void TestRuleEquals() 24 | { 25 | TimeSeriesRule rule = new TimeSeriesRule("key", 50, TsAggregation.Avg); 26 | 27 | TimeSeriesRule rule1 = new TimeSeriesRule("key", 50, TsAggregation.Avg); 28 | TimeSeriesRule rule2 = new TimeSeriesRule("key2", 50, TsAggregation.Avg); 29 | TimeSeriesRule rule3 = new TimeSeriesRule("key", 51, TsAggregation.Avg); 30 | TimeSeriesRule rule4 = new TimeSeriesRule("key", 50, TsAggregation.Count); 31 | 32 | Assert.Equal(rule, rule1); 33 | Assert.NotEqual(rule, rule2); 34 | Assert.NotEqual(rule, rule3); 35 | Assert.NotEqual(rule, rule4); 36 | } 37 | 38 | [Fact] 39 | public void TestRuleHashCode() 40 | { 41 | TimeSeriesRule rule = new TimeSeriesRule("key", 50, TsAggregation.Avg); 42 | 43 | TimeSeriesRule rule1 = new TimeSeriesRule("key", 50, TsAggregation.Avg); 44 | TimeSeriesRule rule2 = new TimeSeriesRule("key2", 50, TsAggregation.Avg); 45 | TimeSeriesRule rule3 = new TimeSeriesRule("key", 51, TsAggregation.Avg); 46 | TimeSeriesRule rule4 = new TimeSeriesRule("key", 50, TsAggregation.Count); 47 | 48 | Assert.Equal(rule.GetHashCode(), rule1.GetHashCode()); 49 | Assert.NotEqual(rule.GetHashCode(), rule2.GetHashCode()); 50 | Assert.NotEqual(rule.GetHashCode(), rule3.GetHashCode()); 51 | Assert.NotEqual(rule.GetHashCode(), rule4.GetHashCode()); 52 | } 53 | 54 | [Fact] 55 | public void TestRuleToString() 56 | { 57 | TimeSeriesRule rule = new TimeSeriesRule("key", 50, TsAggregation.Avg); 58 | Assert.Equal("DestinationKey: key, TimeBucket: 50, Aggregation: AVG", (string)rule); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/MGetExampleAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using StackExchange.Redis; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace NRedisTimeSeries.Example 8 | { 9 | /// 10 | /// Examples for NRedisTimeSeries async API for MGET queries. 11 | /// 12 | internal class MGetAsyncExample 13 | { 14 | /// 15 | /// Example for basic usage of RedisTimeSeries MGET command with a filter. 16 | /// The NRedisTimeSeries SimpleMGetExample returns and IReadOnlyList<(string key, IReadOnlyList labels, TimeSeriesTuple value)> collection. 17 | /// 18 | public static async Task SimpleMGetAsyncExample() 19 | { 20 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 21 | IDatabase db = redis.GetDatabase(); 22 | var filter = new List { "key=value" }; 23 | var results = await db.TimeSeriesMGetAsync(filter); 24 | // Values extraction example. No lables in this case. 25 | foreach (var result in results) 26 | { 27 | Console.WriteLine(result.key); 28 | TimeSeriesTuple value = result.value; 29 | Console.WriteLine(value); 30 | } 31 | redis.Close(); 32 | } 33 | 34 | /// 35 | /// Example for basic usage of RedisTimeSeries MGET command with a filter and WITHLABELS flag. 36 | /// The NRedisTimeSeries SimpleMGetExample returns and IReadOnlyList<(string key, IReadOnlyList labels, TimeSeriesTuple value)> collection. 37 | /// 38 | public static async Task MGetWithLabelsAsyncExample() 39 | { 40 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 41 | IDatabase db = redis.GetDatabase(); 42 | var filter = new List { "key=value" }; 43 | var results = await db.TimeSeriesMGetAsync(filter, withLabels: true); 44 | // Values extraction example. 45 | foreach (var result in results) 46 | { 47 | Console.WriteLine(result.key); 48 | IReadOnlyList labels = result.labels; 49 | foreach(TimeSeriesLabel label in labels){ 50 | Console.WriteLine(label); 51 | } 52 | TimeSeriesTuple value = result.value; 53 | Console.WriteLine(value); 54 | } 55 | redis.Close(); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /NRedisTimeSeries/DataTypes/TimeSeriesRule.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.Commands.Enums; 2 | using NRedisTimeSeries.Extensions; 3 | using System.Collections.Generic; 4 | 5 | namespace NRedisTimeSeries.DataTypes 6 | { 7 | /// 8 | /// A class that represents time-series aggregation rule. 9 | /// 10 | public class TimeSeriesRule 11 | { 12 | /// 13 | /// Rule's destination key. 14 | /// 15 | public string DestKey { get; private set; } 16 | 17 | /// 18 | /// Rule's aggregation time bucket. 19 | /// 20 | public long TimeBucket { get; private set; } 21 | 22 | /// 23 | /// Rule's aggregation type. 24 | /// 25 | public TsAggregation Aggregation { get; private set; } 26 | 27 | /// 28 | /// Builds a time-series aggregation rule 29 | /// 30 | /// Rule's destination key. 31 | /// Rule's aggregation time bucket. 32 | /// Rule's aggregation type. 33 | public TimeSeriesRule(string destKey, long timeBucket, TsAggregation aggregation) => 34 | (DestKey, TimeBucket, Aggregation) = (destKey, timeBucket, aggregation); 35 | 36 | /// 37 | /// Equality of TimeSeriesRule objects 38 | /// 39 | /// Object to compare 40 | /// If two TimeSeriesRule objects are equal 41 | public override bool Equals(object obj) => 42 | obj is TimeSeriesRule rule && 43 | DestKey == rule.DestKey && 44 | TimeBucket == rule.TimeBucket && 45 | Aggregation == rule.Aggregation; 46 | 47 | /// 48 | /// Implicit cast from TimeSeriesRule to string. 49 | /// 50 | /// TimeSeriesRule 51 | public static implicit operator string(TimeSeriesRule tsr) => 52 | string.Format("DestinationKey: {0}, TimeBucket: {1}, Aggregation: {2}", tsr.DestKey, tsr.TimeBucket, tsr.Aggregation.AsArg()); 53 | 54 | /// 55 | /// TimeSeriesRule object hash code. 56 | /// 57 | /// TimeSeriesRule object hash code. 58 | public override int GetHashCode() 59 | { 60 | var hashCode = 1554951643; 61 | hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(DestKey); 62 | hashCode = (hashCode * -1521134295) + TimeBucket.GetHashCode(); 63 | hashCode = (hashCode * -1521134295) + ((int)Aggregation).GetHashCode(); 64 | return hashCode; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestMGetAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Xunit; 5 | 6 | namespace NRedisTimeSeries.Test.TestAPI 7 | { 8 | public class TestMGetAsync : AbstractTimeSeriesTest 9 | { 10 | public TestMGetAsync(RedisFixture redisFixture) : base(redisFixture) { } 11 | 12 | [Fact] 13 | public async Task TestMGetQuery() 14 | { 15 | var keys = CreateKeyNames(2); 16 | var db = redisFixture.Redis.GetDatabase(); 17 | 18 | var label1 = new TimeSeriesLabel(keys[0], "value"); 19 | var label2 = new TimeSeriesLabel(keys[1], "value2"); 20 | var labels1 = new List { label1, label2 }; 21 | var labels2 = new List { label1 }; 22 | 23 | var ts1 = await db.TimeSeriesAddAsync(keys[0], "*", 1.1, labels: labels1); 24 | var tuple1 = new TimeSeriesTuple(ts1, 1.1); 25 | var ts2 = await db.TimeSeriesAddAsync(keys[1], "*", 2.2, labels: labels2); 26 | var tuple2 = new TimeSeriesTuple(ts2, 2.2); 27 | 28 | var results = await db.TimeSeriesMGetAsync(new List { $"{keys[0]}=value" }); 29 | Assert.Equal(2, results.Count); 30 | Assert.Equal(keys[0], results[0].key); 31 | Assert.Equal(tuple1, results[0].value); 32 | Assert.Equal(new List(), results[0].labels); 33 | Assert.Equal(keys[1], results[1].key); 34 | Assert.Equal(tuple2, results[1].value); 35 | Assert.Equal(new List(), results[1].labels); 36 | } 37 | 38 | [Fact] 39 | public async Task TestMGetQueryWithLabels() 40 | { 41 | var keys = CreateKeyNames(2); 42 | var db = redisFixture.Redis.GetDatabase(); 43 | 44 | var label1 = new TimeSeriesLabel(keys[0], "value"); 45 | var label2 = new TimeSeriesLabel(keys[1], "value2"); 46 | var labels1 = new List { label1, label2 }; 47 | var labels2 = new List { label1 }; 48 | 49 | var ts1 = await db.TimeSeriesAddAsync(keys[0], "*", 1.1, labels: labels1); 50 | var tuple1 = new TimeSeriesTuple(ts1, 1.1); 51 | var ts2 = await db.TimeSeriesAddAsync(keys[1], "*", 2.2, labels: labels2); 52 | var tuple2 = new TimeSeriesTuple(ts2, 2.2); 53 | 54 | var results = await db.TimeSeriesMGetAsync(new List { $"{keys[0]}=value" }, withLabels: true); 55 | Assert.Equal(2, results.Count); 56 | Assert.Equal(keys[0], results[0].key); 57 | Assert.Equal(tuple1, results[0].value); 58 | Assert.Equal(labels1, results[0].labels); 59 | Assert.Equal(keys[1], results[1].key); 60 | Assert.Equal(tuple2, results[1].value); 61 | Assert.Equal(labels2, results[1].labels); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/IncrByExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.DataTypes; 4 | using StackExchange.Redis; 5 | 6 | namespace NRedisTimeSeries.Example 7 | { 8 | /// 9 | /// Examples for NRedisTimeSeries API for INCRBY. 10 | /// 11 | internal class IncrByExample 12 | { 13 | /// 14 | /// Example for increasing the value of the last sample by 5. 15 | /// 16 | public static void DefaultIncrByExample() 17 | { 18 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 19 | IDatabase db = redis.GetDatabase(); 20 | db.TimeSeriesIncrBy("my_ts", 5); 21 | redis.Close(); 22 | } 23 | 24 | /// 25 | /// Example for setting the last sample timestamp to system time and its value to 5, with INCRBY. 26 | /// 27 | public static void SystemTimeIncrByExample() 28 | { 29 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 30 | IDatabase db = redis.GetDatabase(); 31 | db.TimeSeriesIncrBy("my_ts", 5, timestamp: "*"); 32 | redis.Close(); 33 | } 34 | 35 | /// 36 | /// Example for setting the last sample timestamp to DateTime.UtcNow and its value to 5, with INCRBY. 37 | /// 38 | public static void DateTimeIncrByExample() 39 | { 40 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 41 | IDatabase db = redis.GetDatabase(); 42 | db.TimeSeriesIncrBy("my_ts", 5, timestamp: DateTime.UtcNow); 43 | redis.Close(); 44 | } 45 | 46 | /// 47 | /// Example for setting the last sample timestamp to long value and its value to 5, with INCRBY. 48 | /// 49 | public static void LongIncrByExample() 50 | { 51 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 52 | IDatabase db = redis.GetDatabase(); 53 | db.TimeSeriesIncrBy("my_ts", 5, timestamp: long.MaxValue); 54 | redis.Close(); 55 | } 56 | 57 | /// 58 | /// Example for setting the last sample timestamp to system time and its value to 5, with INCRBY. 59 | /// The parameters retentionTime, uncompressed and labels are optional and can be set in any order when used as named argument. 60 | /// 61 | public static void ParameterizedIncrByExample() 62 | { 63 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 64 | IDatabase db = redis.GetDatabase(); 65 | var label = new TimeSeriesLabel("key", "value"); 66 | var labels = new List { label }; 67 | db.TimeSeriesIncrBy("my_ts", 5, timestamp: "*", retentionTime:5000, uncompressed:true, labels: labels); 68 | redis.Close(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/DecrByExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.DataTypes; 4 | using StackExchange.Redis; 5 | 6 | namespace NRedisTimeSeries.Example 7 | { 8 | /// 9 | /// Examples for NRedisTimeSeries API for DECRBY. 10 | /// 11 | internal class DecrByExample 12 | { 13 | /// 14 | /// Example for decreasing the value of the last sample by 5. 15 | /// 16 | public static void DefaultDecrByExample() 17 | { 18 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 19 | IDatabase db = redis.GetDatabase(); 20 | db.TimeSeriesDecrBy("my_ts", 5); 21 | redis.Close(); 22 | } 23 | 24 | /// 25 | /// Example for setting the last sample timestamp to system time and its value to -5, with DECRBY. 26 | /// 27 | public static void SystemTimeDecrByExample() 28 | { 29 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 30 | IDatabase db = redis.GetDatabase(); 31 | db.TimeSeriesDecrBy("my_ts", 5, timestamp: "*"); 32 | redis.Close(); 33 | } 34 | 35 | /// 36 | /// Example for setting the last sample timestamp to DateTime.UtcNow and its value to -5, with DECRBY. 37 | /// 38 | public static void DateTimeDecrByExample() 39 | { 40 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 41 | IDatabase db = redis.GetDatabase(); 42 | db.TimeSeriesDecrBy("my_ts", 5, timestamp: DateTime.UtcNow); 43 | redis.Close(); 44 | } 45 | 46 | /// 47 | /// Example for setting the last sample timestamp to long value and its value to -5, with DECRBY. 48 | /// 49 | public static void LongDecrByExample() 50 | { 51 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 52 | IDatabase db = redis.GetDatabase(); 53 | db.TimeSeriesDecrBy("my_ts", 5, timestamp: long.MaxValue); 54 | redis.Close(); 55 | } 56 | 57 | /// 58 | /// Example for setting the last sample timestamp to system time and its value to -5, with DECRBY. 59 | /// The parameters retentionTime, uncompressed and labels are optional and can be set in any order when used as named argument. 60 | /// 61 | public static void ParameterizedDecrByExample() 62 | { 63 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 64 | IDatabase db = redis.GetDatabase(); 65 | var label = new TimeSeriesLabel("key", "value"); 66 | var labels = new List { label }; 67 | db.TimeSeriesDecrBy("my_ts", 5, timestamp: "*", retentionTime: 5000, uncompressed: true, labels: labels); 68 | redis.Close(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/IncrByExampleAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using StackExchange.Redis; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace NRedisTimeSeries.Example 8 | { 9 | /// 10 | /// Examples for NRedisTimeSeries async API for INCRBY. 11 | /// 12 | internal class IncrByAsyncExample 13 | { 14 | /// 15 | /// Example for increasing the value of the last sample by 5. 16 | /// 17 | public static async Task DefaultIncrByAsyncExample() 18 | { 19 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 20 | IDatabase db = redis.GetDatabase(); 21 | await db.TimeSeriesIncrByAsync("my_ts", 5); 22 | redis.Close(); 23 | } 24 | 25 | /// 26 | /// Example for setting the last sample timestamp to system time and its value to 5, with INCRBY. 27 | /// 28 | public static async Task SystemTimeIncrByAsyncExample() 29 | { 30 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 31 | IDatabase db = redis.GetDatabase(); 32 | await db.TimeSeriesIncrByAsync("my_ts", 5, timestamp: "*"); 33 | redis.Close(); 34 | } 35 | 36 | /// 37 | /// Example for setting the last sample timestamp to DateTime.UtcNow and its value to 5, with INCRBY. 38 | /// 39 | public static async Task DateTimeIncrByAsyncExample() 40 | { 41 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 42 | IDatabase db = redis.GetDatabase(); 43 | await db.TimeSeriesIncrByAsync("my_ts", 5, timestamp: DateTime.UtcNow); 44 | redis.Close(); 45 | } 46 | 47 | /// 48 | /// Example for setting the last sample timestamp to long value and its value to 5, with INCRBY. 49 | /// 50 | public static async Task LongIncrByAsyncExample() 51 | { 52 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 53 | IDatabase db = redis.GetDatabase(); 54 | await db.TimeSeriesIncrByAsync("my_ts", 5, timestamp: long.MaxValue); 55 | redis.Close(); 56 | } 57 | 58 | /// 59 | /// Example for setting the last sample timestamp to system time and its value to 5, with INCRBY. 60 | /// The parameters retentionTime, uncompressed and labels are optional and can be set in any order when used as named argument. 61 | /// 62 | public static async Task ParameterizedIncrByAsyncExample() 63 | { 64 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 65 | IDatabase db = redis.GetDatabase(); 66 | var label = new TimeSeriesLabel("key", "value"); 67 | var labels = new List { label }; 68 | await db.TimeSeriesIncrByAsync("my_ts", 5, timestamp: "*", retentionTime: 5000, uncompressed: true, labels: labels); 69 | redis.Close(); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/DecrByExampleAsync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using NRedisTimeSeries.DataTypes; 5 | using StackExchange.Redis; 6 | 7 | namespace NRedisTimeSeries.Example 8 | { 9 | /// 10 | /// Examples for NRedisTimeSeries async API for DECRBY. 11 | /// 12 | internal class DecrByAsyncExample 13 | { 14 | /// 15 | /// Example for decreasing the value of the last sample by 5. 16 | /// 17 | public static async Task DefaultDecrByAsyncExample() 18 | { 19 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 20 | IDatabase db = redis.GetDatabase(); 21 | await db.TimeSeriesDecrByAsync("my_ts", 5); 22 | redis.Close(); 23 | } 24 | 25 | /// 26 | /// Example for setting the last sample timestamp to system time and its value to -5, with DECRBY. 27 | /// 28 | public static async Task SystemTimeDecrByAsyncExample() 29 | { 30 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 31 | IDatabase db = redis.GetDatabase(); 32 | await db.TimeSeriesDecrByAsync("my_ts", 5, timestamp: "*"); 33 | redis.Close(); 34 | } 35 | 36 | /// 37 | /// Example for setting the last sample timestamp to DateTime.UtcNow and its value to -5, with DECRBY. 38 | /// 39 | public static async Task DateTimeDecrByAsyncExample() 40 | { 41 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 42 | IDatabase db = redis.GetDatabase(); 43 | await db.TimeSeriesDecrByAsync("my_ts", 5, timestamp: DateTime.UtcNow); 44 | redis.Close(); 45 | } 46 | 47 | /// 48 | /// Example for setting the last sample timestamp to long value and its value to -5, with DECRBY. 49 | /// 50 | public static async Task LongDecrByAsyncExample() 51 | { 52 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 53 | IDatabase db = redis.GetDatabase(); 54 | await db.TimeSeriesDecrByAsync("my_ts", 5, timestamp: long.MaxValue); 55 | redis.Close(); 56 | } 57 | 58 | /// 59 | /// Example for setting the last sample timestamp to system time and its value to -5, with DECRBY. 60 | /// The parameters retentionTime, uncompressed and labels are optional and can be set in any order when used as named argument. 61 | /// 62 | public static async Task ParameterizedDecrByAsyncExample() 63 | { 64 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 65 | IDatabase db = redis.GetDatabase(); 66 | var label = new TimeSeriesLabel("key", "value"); 67 | var labels = new List { label }; 68 | await db.TimeSeriesDecrByAsync("my_ts", 5, timestamp: "*", retentionTime: 5000, uncompressed: true, labels: labels); 69 | redis.Close(); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestMGet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.DataTypes; 4 | using StackExchange.Redis; 5 | using Xunit; 6 | 7 | namespace NRedisTimeSeries.Test.TestAPI 8 | { 9 | public class TestMGet : AbstractTimeSeriesTest, IDisposable 10 | { 11 | 12 | private readonly string[] keys = { "MGET_TESTS_1", "MGET_TESTS_2" }; 13 | 14 | public TestMGet(RedisFixture redisFixture) : base(redisFixture) { } 15 | 16 | public void Dispose() 17 | { 18 | foreach (string key in keys) 19 | { 20 | redisFixture.Redis.GetDatabase().KeyDelete(key); 21 | } 22 | } 23 | 24 | [Fact] 25 | public void TestMGetQuery() 26 | { 27 | IDatabase db = redisFixture.Redis.GetDatabase(); 28 | 29 | var label1 = new TimeSeriesLabel("MGET_TESTS_1", "value"); 30 | var label2 = new TimeSeriesLabel("MGET_TESTS_2", "value2"); 31 | var labels1 = new List { label1, label2 }; 32 | var labels2 = new List { label1 }; 33 | 34 | TimeStamp ts1 = db.TimeSeriesAdd(keys[0], "*", 1.1, labels: labels1); 35 | TimeSeriesTuple tuple1 = new TimeSeriesTuple(ts1, 1.1); 36 | TimeStamp ts2 = db.TimeSeriesAdd(keys[1], "*", 2.2, labels: labels2); 37 | TimeSeriesTuple tuple2 = new TimeSeriesTuple(ts2, 2.2); 38 | var results = db.TimeSeriesMGet(new List { "MGET_TESTS_1=value" }); 39 | Assert.Equal(2, results.Count); 40 | Assert.Equal(keys[0], results[0].key); 41 | Assert.Equal( tuple1 , results[0].value); 42 | Assert.Equal(new List(), results[0].labels); 43 | Assert.Equal(keys[1], results[1].key); 44 | Assert.Equal(tuple2 , results[1].value); 45 | Assert.Equal(new List(), results[1].labels); 46 | 47 | } 48 | 49 | [Fact] 50 | public void TestMGetQueryWithLabels() 51 | { 52 | IDatabase db = redisFixture.Redis.GetDatabase(); 53 | 54 | var label1 = new TimeSeriesLabel("MGET_TESTS_1", "value"); 55 | var label2 = new TimeSeriesLabel("MGET_TESTS_2", "value2"); 56 | var labels1 = new List { label1, label2 }; 57 | var labels2 = new List { label1 }; 58 | 59 | TimeStamp ts1 = db.TimeSeriesAdd(keys[0], "*", 1.1, labels: labels1); 60 | TimeSeriesTuple tuple1 = new TimeSeriesTuple(ts1, 1.1); 61 | TimeStamp ts2 = db.TimeSeriesAdd(keys[1], "*", 2.2, labels: labels2); 62 | TimeSeriesTuple tuple2 = new TimeSeriesTuple(ts2, 2.2); 63 | 64 | var results = db.TimeSeriesMGet(new List { "MGET_TESTS_1=value" }, withLabels: true); 65 | Assert.Equal(2, results.Count); 66 | Assert.Equal(keys[0], results[0].key); 67 | Assert.Equal(tuple1, results[0].value); 68 | Assert.Equal(labels1, results[0].labels); 69 | Assert.Equal(keys[1], results[1].key); 70 | Assert.Equal(tuple2 , results[1].value); 71 | Assert.Equal(labels2, results[1].labels); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/AddExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.DataTypes; 4 | using NRedisTimeSeries.Commands.Enums; 5 | using StackExchange.Redis; 6 | 7 | namespace NRedisTimeSeries.Example 8 | { 9 | /// 10 | /// Examples for NRedisTimeSeries API for adding new sample to time series. 11 | /// 12 | internal class AddExample 13 | { 14 | /// 15 | /// Example for using RedisTimeSeries default "*" charecter for system time. 16 | /// The TimeSeriesAdd method gets a TimeStamp type parameter, which in this case the string "*" 17 | /// is implicitly casted into a new TimeStamp object. 18 | /// 19 | public static void DefaultAdd() 20 | { 21 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 22 | IDatabase db = redis.GetDatabase(); 23 | TimeStamp timestamp = "*"; 24 | db.TimeSeriesAdd("my_ts", timestamp, 0.0); 25 | redis.Close(); 26 | } 27 | 28 | /// 29 | /// Example for using TimeStamp as long value. 30 | /// The TimeSeriesAdd method gets a TimeStamp type parameter, which in this case the value 1 31 | /// is implicitly casted into a new TimeStamp object. 32 | /// 33 | public static void LongAdd() 34 | { 35 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 36 | IDatabase db = redis.GetDatabase(); 37 | TimeStamp timestamp = 1; 38 | db.TimeSeriesAdd("my_ts", timestamp, 0.0); 39 | redis.Close(); 40 | } 41 | 42 | /// 43 | /// Example for using TimeStamp as DateTime value. 44 | /// The TimeSeriesAdd method gets a TimeStamp type parameter, which in this case the value DateTime.UtcNow 45 | /// is implicitly casted into a new TimeStamp object. 46 | /// 47 | public static void DateTimeAdd() 48 | { 49 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 50 | IDatabase db = redis.GetDatabase(); 51 | TimeStamp timestamp = DateTime.UtcNow; 52 | db.TimeSeriesAdd("my_ts", timestamp, 0.0); 53 | redis.Close(); 54 | } 55 | 56 | /// 57 | /// Example for back-fill time-series sample. 58 | /// In order to avoid the BLOCK mode exeption must specify the duplicate policy. 59 | /// 60 | public static void DuplicatedAdd() 61 | { 62 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 63 | IDatabase db = redis.GetDatabase(); 64 | TimeStamp timestamp = DateTime.UtcNow; 65 | db.TimeSeriesAdd("my_ts", timestamp, 1); 66 | db.TimeSeriesAdd("my_ts", timestamp, 0, duplicatePolicy: TsDuplicatePolicy.MIN); 67 | redis.Close(); 68 | } 69 | 70 | /// 71 | /// Example for time-series creation parameters with ADD. 72 | /// Named arguments are used in the same manner of TimeSeriesCreate. 73 | /// 74 | public static void ParameterizedAdd() 75 | { 76 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 77 | IDatabase db = redis.GetDatabase(); 78 | TimeStamp timestamp = "*"; 79 | var label = new TimeSeriesLabel("key", "value"); 80 | var labels = new List { label }; 81 | db.TimeSeriesAdd("my_ts", timestamp, 0.0, retentionTime:5000, labels:labels, uncompressed:true); 82 | redis.Close(); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestIncrBy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.DataTypes; 4 | using StackExchange.Redis; 5 | using Xunit; 6 | 7 | namespace NRedisTimeSeries.Test.TestAPI 8 | { 9 | public class TestIncrBy : AbstractTimeSeriesTest, IDisposable 10 | { 11 | private readonly string key = "INCRBY_TESTS"; 12 | 13 | public TestIncrBy(RedisFixture redisFixture) : base(redisFixture) { } 14 | 15 | public void Dispose() 16 | { 17 | redisFixture.Redis.GetDatabase().KeyDelete(key); 18 | } 19 | 20 | [Fact] 21 | public void TestDefaultIncrBy() 22 | { 23 | double value = 5.5; 24 | IDatabase db = redisFixture.Redis.GetDatabase(); 25 | Assert.True(db.TimeSeriesIncrBy(key, value) > 0); 26 | Assert.Equal(value, db.TimeSeriesGet(key).Val); 27 | } 28 | 29 | [Fact] 30 | public void TestStarIncrBy() 31 | { 32 | double value = 5.5; 33 | IDatabase db = redisFixture.Redis.GetDatabase(); 34 | Assert.True(db.TimeSeriesIncrBy(key, value, timestamp: "*") > 0); 35 | Assert.Equal(value, db.TimeSeriesGet(key).Val); 36 | } 37 | 38 | [Fact] 39 | public void TestIncrByTimeStamp() 40 | { 41 | double value = 5.5; 42 | IDatabase db = redisFixture.Redis.GetDatabase(); 43 | TimeStamp timeStamp = DateTime.UtcNow; 44 | Assert.Equal(timeStamp, db.TimeSeriesIncrBy(key, value, timestamp: timeStamp)); 45 | Assert.Equal(new TimeSeriesTuple(timeStamp, value), db.TimeSeriesGet(key)); 46 | } 47 | 48 | [Fact] 49 | public void TestDefaultIncrByWithRetentionTime() 50 | { 51 | double value = 5.5; 52 | long retentionTime = 5000; 53 | IDatabase db = redisFixture.Redis.GetDatabase(); 54 | Assert.True(db.TimeSeriesIncrBy(key, value, retentionTime: retentionTime) > 0); 55 | Assert.Equal(value, db.TimeSeriesGet(key).Val); 56 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 57 | Assert.Equal(retentionTime, info.RetentionTime); 58 | } 59 | 60 | [Fact] 61 | public void TestDefaultIncrByWithLabels() 62 | { 63 | double value = 5.5; 64 | TimeSeriesLabel label = new TimeSeriesLabel("key", "value"); 65 | IDatabase db = redisFixture.Redis.GetDatabase(); 66 | var labels = new List { label }; 67 | Assert.True(db.TimeSeriesIncrBy(key, value, labels: labels) > 0); 68 | Assert.Equal(value, db.TimeSeriesGet(key).Val); 69 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 70 | Assert.Equal(labels, info.Labels); 71 | } 72 | 73 | [Fact] 74 | public void TestDefaultIncrByWithUncompressed() 75 | { 76 | double value = 5.5; 77 | IDatabase db = redisFixture.Redis.GetDatabase(); 78 | Assert.True(db.TimeSeriesIncrBy(key, value, uncompressed:true) > 0); 79 | Assert.Equal(value, db.TimeSeriesGet(key).Val); 80 | } 81 | 82 | [Fact] 83 | public void TestWrongParameters() 84 | { 85 | double value = 5.5; 86 | IDatabase db = redisFixture.Redis.GetDatabase(); 87 | var ex = Assert.Throws(() => db.TimeSeriesIncrBy(key, value, timestamp: "+")); 88 | Assert.Equal("ERR TSDB: invalid timestamp", ex.Message); 89 | ex = Assert.Throws(() => db.TimeSeriesIncrBy(key, value, timestamp: "-")); 90 | Assert.Equal("ERR TSDB: invalid timestamp", ex.Message); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestDecrBy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.DataTypes; 4 | using StackExchange.Redis; 5 | using Xunit; 6 | 7 | namespace NRedisTimeSeries.Test.TestAPI 8 | { 9 | public class TestDecrBy : AbstractTimeSeriesTest, IDisposable 10 | { 11 | private readonly string key = "DECRBY_TESTS"; 12 | 13 | public TestDecrBy(RedisFixture redisFixture) : base(redisFixture) {} 14 | 15 | public void Dispose() 16 | { 17 | redisFixture.Redis.GetDatabase().KeyDelete(key); 18 | } 19 | 20 | [Fact] 21 | public void TestDefaultDecrBy() 22 | { 23 | double value = 5.5; 24 | IDatabase db = redisFixture.Redis.GetDatabase(); 25 | Assert.True(db.TimeSeriesDecrBy(key, -value) > 0); 26 | Assert.Equal(value, db.TimeSeriesGet(key).Val); 27 | } 28 | 29 | [Fact] 30 | public void TestStarDecrBy() 31 | { 32 | double value = 5.5; 33 | IDatabase db = redisFixture.Redis.GetDatabase(); 34 | Assert.True(db.TimeSeriesDecrBy(key, -value, timestamp: "*") > 0); 35 | Assert.Equal(value, db.TimeSeriesGet(key).Val); 36 | } 37 | 38 | [Fact] 39 | public void TestDecrByTimeStamp() 40 | { 41 | double value = 5.5; 42 | IDatabase db = redisFixture.Redis.GetDatabase(); 43 | TimeStamp timeStamp = DateTime.UtcNow; 44 | Assert.Equal(timeStamp, db.TimeSeriesDecrBy(key, -value, timestamp: timeStamp)); 45 | Assert.Equal(new TimeSeriesTuple(timeStamp, value), db.TimeSeriesGet(key)); 46 | } 47 | 48 | [Fact] 49 | public void TestDefaultDecrByWithRetentionTime() 50 | { 51 | double value = 5.5; 52 | long retentionTime = 5000; 53 | IDatabase db = redisFixture.Redis.GetDatabase(); 54 | Assert.True(db.TimeSeriesDecrBy(key, -value, retentionTime: retentionTime) > 0); 55 | Assert.Equal(value, db.TimeSeriesGet(key).Val); 56 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 57 | Assert.Equal(retentionTime, info.RetentionTime); 58 | } 59 | 60 | [Fact] 61 | public void TestDefaultDecrByWithLabels() 62 | { 63 | double value = 5.5; 64 | TimeSeriesLabel label = new TimeSeriesLabel("key", "value"); 65 | IDatabase db = redisFixture.Redis.GetDatabase(); 66 | var labels = new List { label }; 67 | Assert.True(db.TimeSeriesDecrBy(key, -value, labels: labels) > 0); 68 | Assert.Equal(value, db.TimeSeriesGet(key).Val); 69 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 70 | Assert.Equal(labels, info.Labels); 71 | } 72 | 73 | [Fact] 74 | public void TestDefaultDecrByWithUncompressed() 75 | { 76 | double value = 5.5; 77 | IDatabase db = redisFixture.Redis.GetDatabase(); 78 | Assert.True(db.TimeSeriesDecrBy(key, -value, uncompressed: true) > 0); 79 | Assert.Equal(value, db.TimeSeriesGet(key).Val); 80 | } 81 | 82 | [Fact] 83 | public void TestWrongParameters() 84 | { 85 | double value = 5.5; 86 | IDatabase db = redisFixture.Redis.GetDatabase(); 87 | var ex = Assert.Throws(() => db.TimeSeriesDecrBy(key, value, timestamp: "+")); 88 | Assert.Equal("ERR TSDB: invalid timestamp", ex.Message); 89 | ex = Assert.Throws(() => db.TimeSeriesDecrBy(key, value, timestamp: "-")); 90 | Assert.Equal("ERR TSDB: invalid timestamp", ex.Message); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestRulesAsync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using StackExchange.Redis; 6 | using NRedisTimeSeries.Commands.Enums; 7 | using NRedisTimeSeries.DataTypes; 8 | using Xunit; 9 | 10 | namespace NRedisTimeSeries.Test.TestAPI 11 | { 12 | public class TestRulesAsync : AbstractTimeSeriesTest 13 | { 14 | public TestRulesAsync(RedisFixture redisFixture) : base(redisFixture) { } 15 | 16 | [Fact] 17 | public async Task TestRulesAdditionDeletion() 18 | { 19 | var key = CreateKeyName(); 20 | var db = redisFixture.Redis.GetDatabase(); 21 | await db.TimeSeriesCreateAsync(key); 22 | var aggregations = (TsAggregation[])Enum.GetValues(typeof(TsAggregation)); 23 | 24 | foreach (var aggregation in aggregations) 25 | { 26 | await db.TimeSeriesCreateAsync($"{key}:{aggregation}"); 27 | } 28 | 29 | var timeBucket = 50L; 30 | var rules = new List(); 31 | var rulesMap = new Dictionary(); 32 | foreach (var aggregation in aggregations) 33 | { 34 | var rule = new TimeSeriesRule($"{key}:{aggregation}", timeBucket, aggregation); 35 | rules.Add(rule); 36 | rulesMap[aggregation] = rule; 37 | Assert.True(await db.TimeSeriesCreateRuleAsync(key, rule)); 38 | 39 | var info = await db.TimeSeriesInfoAsync(key); 40 | Assert.Equal(rules, info.Rules); 41 | } 42 | 43 | foreach (var aggregation in aggregations) 44 | { 45 | var rule = rulesMap[aggregation]; 46 | rules.Remove(rule); 47 | Assert.True(await db.TimeSeriesDeleteRuleAsync(key, rule.DestKey)); 48 | 49 | var info = await db.TimeSeriesInfoAsync(key); 50 | Assert.Equal(rules, info.Rules); 51 | } 52 | 53 | await db.KeyDeleteAsync(aggregations.Select(i => (RedisKey)$"{key}:{i}").ToArray()); 54 | } 55 | 56 | [Fact] 57 | public async Task TestNonExistingSrc() 58 | { 59 | var key = CreateKeyName(); 60 | var aggKey = $"{key}:{TsAggregation.Avg}"; 61 | var db = redisFixture.Redis.GetDatabase(); 62 | await db.TimeSeriesCreateAsync(aggKey); 63 | var rule = new TimeSeriesRule(aggKey, 50, TsAggregation.Avg); 64 | var ex = await Assert.ThrowsAsync(async () => await db.TimeSeriesCreateRuleAsync(key, rule)); 65 | Assert.Equal("ERR TSDB: the key does not exist", ex.Message); 66 | 67 | ex = await Assert.ThrowsAsync(async () => await db.TimeSeriesDeleteRuleAsync(key, aggKey)); 68 | Assert.Equal("ERR TSDB: the key does not exist", ex.Message); 69 | 70 | await db.KeyDeleteAsync(aggKey); 71 | } 72 | 73 | [Fact] 74 | public async Task TestNonExisitingDestinaion() 75 | { 76 | var key = CreateKeyName(); 77 | var aggKey = $"{key}:{TsAggregation.Avg}"; 78 | var db = redisFixture.Redis.GetDatabase(); 79 | await db.TimeSeriesCreateAsync(key); 80 | var rule = new TimeSeriesRule(aggKey, 50, TsAggregation.Avg); 81 | var ex = await Assert.ThrowsAsync(async () => await db.TimeSeriesCreateRuleAsync(key, rule)); 82 | Assert.Equal("ERR TSDB: the key does not exist", ex.Message); 83 | 84 | ex = await Assert.ThrowsAsync(async () => await db.TimeSeriesDeleteRuleAsync(key, aggKey)); 85 | Assert.Equal("ERR TSDB: compaction rule does not exist", ex.Message); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/AddExampleAsync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using StackExchange.Redis; 5 | using NRedisTimeSeries.Commands.Enums; 6 | using NRedisTimeSeries.DataTypes; 7 | 8 | namespace NRedisTimeSeries.Example 9 | { 10 | /// 11 | /// Examples for NRedisTimeSeries async API for adding new sample to time series. 12 | /// 13 | internal class AddAsyncExample 14 | { 15 | /// 16 | /// Example for using RedisTimeSeries default "*" charecter for system time. 17 | /// The TimeSeriesAdd method gets a TimeStamp type parameter, which in this case the string "*" 18 | /// is implicitly casted into a new TimeStamp object. 19 | /// 20 | public static async Task DefaultAddAsync() 21 | { 22 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 23 | IDatabase db = redis.GetDatabase(); 24 | TimeStamp timestamp = "*"; 25 | await db.TimeSeriesAddAsync("my_ts", timestamp, 0.0); 26 | redis.Close(); 27 | } 28 | 29 | /// 30 | /// Example for using TimeStamp as long value. 31 | /// The TimeSeriesAdd method gets a TimeStamp type parameter, which in this case the value 1 32 | /// is implicitly casted into a new TimeStamp object. 33 | /// 34 | public static async Task LongAddAsync() 35 | { 36 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 37 | IDatabase db = redis.GetDatabase(); 38 | TimeStamp timestamp = 1; 39 | await db.TimeSeriesAddAsync("my_ts", timestamp, 0.0); 40 | redis.Close(); 41 | } 42 | 43 | /// 44 | /// Example for using TimeStamp as DateTime value. 45 | /// The TimeSeriesAdd method gets a TimeStamp type parameter, which in this case the value DateTime.UtcNow 46 | /// is implicitly casted into a new TimeStamp object. 47 | /// 48 | public static async Task DateTimeAddAsync() 49 | { 50 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 51 | IDatabase db = redis.GetDatabase(); 52 | TimeStamp timestamp = DateTime.UtcNow; 53 | await db.TimeSeriesAddAsync("my_ts", timestamp, 0.0); 54 | redis.Close(); 55 | } 56 | 57 | /// 58 | /// Example for back-fill time-series sample. 59 | /// In order to avoid the BLOCK mode exeption must specify the duplicate policy. 60 | /// 61 | public static async void DuplicatedAddAsync() 62 | { 63 | 64 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 65 | IDatabase db = redis.GetDatabase(); 66 | TimeStamp timestamp = DateTime.UtcNow; 67 | await db.TimeSeriesAddAsync("my_ts", timestamp, 1); 68 | await db.TimeSeriesAddAsync("my_ts", timestamp, 0, duplicatePolicy: TsDuplicatePolicy.MIN); 69 | redis.Close(); 70 | } 71 | 72 | /// 73 | /// Example for time-series creation parameters with ADD. 74 | /// Named arguments are used in the same manner of TimeSeriesCreate. 75 | /// 76 | public static async Task ParameterizedAddAsync() 77 | { 78 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 79 | IDatabase db = redis.GetDatabase(); 80 | TimeStamp timestamp = "*"; 81 | var label = new TimeSeriesLabel("key", "value"); 82 | var labels = new List { label }; 83 | await db.TimeSeriesAddAsync("my_ts", timestamp, 0.0, retentionTime:5000, labels:labels, uncompressed:true); 84 | redis.Close(); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![license](https://img.shields.io/github/license/RedisTimeSeries/NRedisTimeSeries.svg)](https://github.com/RedisTimeSeries/NRedisTimeSeries) 2 | [![CircleCI](https://circleci.com/gh/RedisTimeSeries/NRedisTimeSeries/tree/master.svg?style=svg)](https://circleci.com/gh/RedisTimeSeries/NRedisTimeSeries/tree/master) 3 | [![GitHub issues](https://img.shields.io/github/release/RedisTimeSeries/NRedisTimeSeries.svg)](https://github.com/RedisTimeSeries/NRedisTimeSeries/releases/latest) 4 | [![Codecov](https://codecov.io/gh/RedisTimeSeries/NRedisTimeSeries/branch/master/graph/badge.svg)](https://codecov.io/gh/RedisTimeSeries/NRedisTimeSeries) 5 | [![Known Vulnerabilities](https://snyk.io/test/github/RedisTimeSeries/NRedisTimeSeries/badge.svg?targetFile=NRedisTimeSeries/NRedisTimeSeries.csproj)](https://snyk.io/test/github/RedisTimeSeries/NRedisTimeSeries?targetFile=NRedisTimeSeries/NRedisTimeSeries.csproj) 6 | [![StackExchange.Redis](https://img.shields.io/nuget/v/NRedisTimeSeries.svg)](https://www.nuget.org/packages/NRedisTimeSeries/) 7 | 8 | # NRedisTimeSeries 9 | [![Forum](https://img.shields.io/badge/Forum-RedisTimeSeries-blue)](https://forum.redislabs.com/c/modules/redistimeseries) 10 | [![Discord](https://img.shields.io/discord/697882427875393627?style=flat-square)](https://discord.gg/KExRgMb) 11 | 12 | .Net Client for RedisTimeSeries 13 | 14 | ## Deprecation notice 15 | 16 | As of [nredisstack 0.4.1](https://github.com/redis/nredisstack) this library is deprecated. It's features have been merged into [nredisstack](https://github.com/redis/nredisstack. Please either install it [from nuget](https://www.nuget.org/packages/NRedisStack) or [the repo](https://github.com/redis/nredisstack). 17 | 18 | 19 | ## API 20 | The complete documentation of RedisTimeSeries's commands can be found at [RedisTimeSeries's website](http://redistimeseries.io/). 21 | 22 | ## Usage example 23 | 24 | ```C# 25 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 26 | IDatabase db = redis.GetDatabase(); 27 | 28 | // Create 29 | var label = new TimeSeriesLabel("Time", "Series"); 30 | db.TimeSeriesCreate("test", retentionTime: 5000, labels: new List { label }, duplicatePolicy: TsDuplicatePolicy.MAX); 31 | 32 | // Alter 33 | label = new TimeSeriesLabel("Alter", "label"); 34 | db.TimeSeriesAlter("test", retentionTime: 0, labels: new List { label }); 35 | 36 | // Add 37 | db.TimeSeriesAdd("test", 1, 1.12); 38 | db.TimeSeriesAdd("test", 1, 1.13, duplicatePolicy: TsDuplicatePolicy.LAST); 39 | 40 | // MAdd 41 | var sequence = new List<(string, TimeStamp, double)>(3); 42 | sequence.Add(("test", "*", 0.0)); 43 | sequence.Add(("test", DateTime.UtcNow, 0.0)); 44 | sequence.Add(("test", 1, 1.0)); 45 | db.TimeSeriesMAdd(sequence); 46 | 47 | // Rule 48 | db.TimeSeriesCreate("sumRule"); 49 | TimeSeriesRule rule = new TimeSeriesRule("sumRule", 20, TsAggregation.Sum); 50 | db.TimeSeriesCreateRule("test", rule); 51 | db.TimeSeriesAdd("test", "*", 1); 52 | db.TimeSeriesAdd("test", "*", 2); 53 | db.TimeSeriesDeleteRule("test", "sumRule"); 54 | db.KeyDelete("sumRule"); 55 | 56 | // Range 57 | db.TimeSeriesRange("test", "-", "+"); 58 | db.TimeSeriesRange("test", "-", "+", aggregation: TsAggregation.Avg, timeBucket: 10); 59 | 60 | // Get 61 | db.TimeSeriesGet("test"); 62 | 63 | // Info 64 | TimeSeriesInformation info = db.TimeSeriesInfo("test"); 65 | 66 | // DEL 67 | db.KeyDelete("test"); 68 | ``` 69 | 70 | ## Further notes on back-filling time series 71 | 72 | Since [RedisTimeSeries 1.4](https://github.com/RedisTimeSeries/RedisTimeSeries/releases/tag/v1.4.5) we've added the ability to back-fill time series, with different duplicate policies. 73 | 74 | The default behavior is to block updates to the same timestamp, and you can control it via the `duplicatePolicy` argument. You can check in detail the [duplicate policy documentation](https://oss.redislabs.com/redistimeseries/configuration/#duplicate_policy). 75 | 76 | 77 | See the [Example project](NRedisTimeSeries.Example) for commands reference -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestCreate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using StackExchange.Redis; 4 | using NRedisTimeSeries; 5 | using NRedisTimeSeries.DataTypes; 6 | using NRedisTimeSeries.Commands; 7 | using NRedisTimeSeries.Commands.Enums; 8 | using Xunit; 9 | 10 | namespace NRedisTimeSeries.Test.TestAPI 11 | { 12 | public class TestCreate : AbstractTimeSeriesTest, IDisposable 13 | { 14 | private readonly string key = "CREATE_TESTS"; 15 | 16 | public TestCreate(RedisFixture redisFixture) : base(redisFixture) { } 17 | 18 | public void Dispose() 19 | { 20 | redisFixture.Redis.GetDatabase().KeyDelete(key); 21 | } 22 | 23 | [Fact] 24 | public void TestCreateOK() 25 | { 26 | IDatabase db = redisFixture.Redis.GetDatabase(); 27 | Assert.True(db.TimeSeriesCreate(key)); 28 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 29 | } 30 | 31 | [Fact] 32 | public void TestCreateRetentionTime() 33 | { 34 | long retentionTime = 5000; 35 | IDatabase db = redisFixture.Redis.GetDatabase(); 36 | Assert.True(db.TimeSeriesCreate(key, retentionTime: retentionTime)); 37 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 38 | Assert.Equal(retentionTime, info.RetentionTime); 39 | } 40 | 41 | [Fact] 42 | public void TestCreateLabels() 43 | { 44 | TimeSeriesLabel label = new TimeSeriesLabel("key", "value"); 45 | var labels = new List { label }; 46 | IDatabase db = redisFixture.Redis.GetDatabase(); 47 | Assert.True(db.TimeSeriesCreate(key, labels: labels)); 48 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 49 | Assert.Equal(labels, info.Labels); 50 | } 51 | 52 | [Fact] 53 | public void TestCreateEmptyLabels() 54 | { 55 | var labels = new List(); 56 | IDatabase db = redisFixture.Redis.GetDatabase(); 57 | Assert.True(db.TimeSeriesCreate(key, labels: labels)); 58 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 59 | Assert.Equal(labels, info.Labels); 60 | } 61 | 62 | [Fact] 63 | public void TestCreateUncompressed() 64 | { 65 | IDatabase db = redisFixture.Redis.GetDatabase(); 66 | Assert.True(db.TimeSeriesCreate(key, uncompressed: true)); 67 | } 68 | 69 | [Fact] 70 | public void TestCreatehDuplicatePolicyFirst() 71 | { 72 | IDatabase db = redisFixture.Redis.GetDatabase(); 73 | Assert.True(db.TimeSeriesCreate(key, duplicatePolicy: TsDuplicatePolicy.FIRST)); 74 | } 75 | 76 | [Fact] 77 | public void TestCreatehDuplicatePolicyLast() 78 | { 79 | IDatabase db = redisFixture.Redis.GetDatabase(); 80 | Assert.True(db.TimeSeriesCreate(key, duplicatePolicy: TsDuplicatePolicy.LAST)); 81 | } 82 | 83 | [Fact] 84 | public void TestCreatehDuplicatePolicyMin() 85 | { 86 | IDatabase db = redisFixture.Redis.GetDatabase(); 87 | Assert.True(db.TimeSeriesCreate(key, duplicatePolicy: TsDuplicatePolicy.MIN)); 88 | } 89 | 90 | [Fact] 91 | public void TestCreatehDuplicatePolicyMax() 92 | { 93 | IDatabase db = redisFixture.Redis.GetDatabase(); 94 | Assert.True(db.TimeSeriesCreate(key, duplicatePolicy: TsDuplicatePolicy.MAX)); 95 | } 96 | 97 | [Fact] 98 | public void TestCreatehDuplicatePolicySum() 99 | { 100 | IDatabase db = redisFixture.Redis.GetDatabase(); 101 | Assert.True(db.TimeSeriesCreate(key, duplicatePolicy: TsDuplicatePolicy.SUM)); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestMAddAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using StackExchange.Redis; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | 8 | namespace NRedisTimeSeries.Test.TestAPI 9 | { 10 | public class TestMAddAsync : AbstractTimeSeriesTest 11 | { 12 | public TestMAddAsync(RedisFixture redisFixture) : base(redisFixture) { } 13 | 14 | 15 | [Fact] 16 | public async Task TestStarMADD() 17 | { 18 | var keys = CreateKeyNames(2); 19 | 20 | IDatabase db = redisFixture.Redis.GetDatabase(); 21 | 22 | foreach (string key in keys) 23 | { 24 | await db.TimeSeriesCreateAsync(key); 25 | } 26 | 27 | List<(string, TimeStamp, double)> sequence = new List<(string, TimeStamp, double)>(keys.Length); 28 | foreach (var keyname in keys) 29 | { 30 | sequence.Add((keyname, "*", 1.1)); 31 | } 32 | var response = await db.TimeSeriesMAddAsync(sequence); 33 | 34 | Assert.Equal(keys.Length, response.Count); 35 | 36 | foreach (var key in keys) 37 | { 38 | TimeSeriesInformation info = await db.TimeSeriesInfoAsync(key); 39 | Assert.True(info.FirstTimeStamp > 0); 40 | Assert.Equal(info.FirstTimeStamp, info.LastTimeStamp); 41 | } 42 | } 43 | 44 | 45 | [Fact] 46 | public async Task TestSuccessfulMAdd() 47 | { 48 | var keys = CreateKeyNames(2); 49 | var db = redisFixture.Redis.GetDatabase(); 50 | 51 | foreach (var key in keys) 52 | { 53 | await db.TimeSeriesCreateAsync(key); 54 | } 55 | 56 | var sequence = new List<(string, TimeStamp, double)>(keys.Length); 57 | var timestamps = new List(keys.Length); 58 | foreach (var keyname in keys) 59 | { 60 | var now = DateTime.UtcNow; 61 | timestamps.Add(now); 62 | sequence.Add((keyname, now, 1.1)); 63 | } 64 | 65 | var response = await db.TimeSeriesMAddAsync(sequence); 66 | Assert.Equal(timestamps.Count, response.Count); 67 | for (var i = 0; i < response.Count; i++) 68 | { 69 | Assert.Equal(timestamps[i], response[i]); 70 | } 71 | } 72 | 73 | [Fact] 74 | public async Task TestOverrideMAdd() 75 | { 76 | var keys = CreateKeyNames(2); 77 | var db = redisFixture.Redis.GetDatabase(); 78 | 79 | foreach (var key in keys) 80 | { 81 | await db.TimeSeriesCreateAsync(key); 82 | } 83 | 84 | var oldTimeStamps = new List(); 85 | foreach (var keyname in keys) 86 | { 87 | oldTimeStamps.Add(DateTime.UtcNow); 88 | } 89 | 90 | var sequence = new List<(string, TimeStamp, double)>(keys.Length); 91 | foreach (var keyname in keys) 92 | { 93 | sequence.Add((keyname, DateTime.UtcNow, 1.1)); 94 | } 95 | 96 | await db.TimeSeriesMAddAsync(sequence); 97 | sequence.Clear(); 98 | 99 | // Override the same events should not throw an error 100 | for (var i = 0; i < keys.Length; i++) 101 | { 102 | sequence.Add((keys[i], oldTimeStamps[i], 1.1)); 103 | } 104 | 105 | var response = await db.TimeSeriesMAddAsync(sequence); 106 | 107 | Assert.Equal(oldTimeStamps.Count, response.Count); 108 | for(int i = 0; i < response.Count; i++) 109 | { 110 | Assert.Equal(oldTimeStamps[i], response[i]); 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestMADD.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.DataTypes; 4 | using StackExchange.Redis; 5 | using Xunit; 6 | 7 | namespace NRedisTimeSeries.Test.TestAPI 8 | { 9 | public class TestMADD : AbstractTimeSeriesTest, IDisposable 10 | { 11 | 12 | private readonly string[] keys = { "MADD_TESTS_1", "MADD_TESTS_2" }; 13 | 14 | public TestMADD(RedisFixture redisFixture) : base(redisFixture) { } 15 | 16 | public void Dispose() 17 | { 18 | foreach(string key in keys) 19 | { 20 | redisFixture.Redis.GetDatabase().KeyDelete(key); 21 | } 22 | } 23 | 24 | [Fact] 25 | public void TestStarMADD() 26 | { 27 | 28 | IDatabase db = redisFixture.Redis.GetDatabase(); 29 | 30 | foreach (string key in keys) 31 | { 32 | db.TimeSeriesCreate(key); 33 | } 34 | List<(string, TimeStamp, double)> sequence = new List<(string, TimeStamp, double)>(keys.Length); 35 | foreach (var keyname in keys) 36 | { 37 | sequence.Add((keyname, "*", 1.1)); 38 | } 39 | var response = db.TimeSeriesMAdd(sequence); 40 | 41 | Assert.Equal(keys.Length, response.Count); 42 | 43 | foreach (var key in keys) 44 | { 45 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 46 | Assert.True(info.FirstTimeStamp > 0); 47 | Assert.Equal(info.FirstTimeStamp, info.LastTimeStamp); 48 | } 49 | } 50 | 51 | [Fact] 52 | public void TestSuccessfulMADD() 53 | { 54 | 55 | IDatabase db = redisFixture.Redis.GetDatabase(); 56 | 57 | foreach (string key in keys) 58 | { 59 | db.TimeSeriesCreate(key); 60 | } 61 | 62 | List<(string, TimeStamp, double)> sequence = new List<(string, TimeStamp, double)>(keys.Length); 63 | List timestamps = new List(keys.Length); 64 | foreach (var keyname in keys) 65 | { 66 | DateTime now = DateTime.UtcNow; 67 | timestamps.Add(now); 68 | sequence.Add((keyname, now, 1.1)); 69 | } 70 | var response = db.TimeSeriesMAdd(sequence); 71 | 72 | Assert.Equal(timestamps.Count, response.Count); 73 | for(int i = 0; i < response.Count; i++) 74 | { 75 | Assert.Equal(timestamps[i], response[i]); 76 | } 77 | } 78 | 79 | [Fact] 80 | public void TestOverrideMADD() 81 | { 82 | IDatabase db = redisFixture.Redis.GetDatabase(); 83 | 84 | foreach (string key in keys) 85 | { 86 | db.TimeSeriesCreate(key); 87 | } 88 | 89 | List oldTimeStamps = new List(); 90 | foreach (var keyname in keys) 91 | { 92 | oldTimeStamps.Add(DateTime.UtcNow); 93 | } 94 | 95 | List<(string, TimeStamp, double)> sequence = new List<(string, TimeStamp, double)>(keys.Length); 96 | foreach (var keyname in keys) 97 | { 98 | sequence.Add((keyname, DateTime.UtcNow, 1.1)); 99 | } 100 | db.TimeSeriesMAdd(sequence); 101 | 102 | sequence.Clear(); 103 | 104 | // Override the same events should not throw an error 105 | for (int i =0; i < keys.Length; i++) 106 | { 107 | sequence.Add((keys[i], oldTimeStamps[i], 1.1)); 108 | } 109 | var response = db.TimeSeriesMAdd(sequence); 110 | 111 | Assert.Equal(oldTimeStamps.Count, response.Count); 112 | for(int i = 0; i < response.Count; i++) 113 | { 114 | Assert.Equal(oldTimeStamps[i], response[i]); 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestCreateAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using NRedisTimeSeries.Commands.Enums; 3 | using NRedisTimeSeries.Commands; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | 8 | namespace NRedisTimeSeries.Test.TestAPI 9 | { 10 | public class TestCreateAsync : AbstractTimeSeriesTest 11 | { 12 | public TestCreateAsync(RedisFixture redisFixture) : base(redisFixture) { } 13 | 14 | [Fact] 15 | public async Task TestCreateOK() 16 | { 17 | var key = CreateKeyName(); 18 | var db = redisFixture.Redis.GetDatabase(); 19 | Assert.True(await db.TimeSeriesCreateAsync(key)); 20 | } 21 | 22 | [Fact] 23 | public async Task TestCreateRetentionTime() 24 | { 25 | var key = CreateKeyName(); 26 | long retentionTime = 5000; 27 | var db = redisFixture.Redis.GetDatabase(); 28 | Assert.True(await db.TimeSeriesCreateAsync(key, retentionTime: retentionTime)); 29 | 30 | var info = await db.TimeSeriesInfoAsync(key); 31 | Assert.Equal(retentionTime, info.RetentionTime); 32 | } 33 | 34 | [Fact] 35 | public async Task TestCreateLabels() 36 | { 37 | var key = CreateKeyName(); 38 | var label = new TimeSeriesLabel("key", "value"); 39 | var labels = new List { label }; 40 | var db = redisFixture.Redis.GetDatabase(); 41 | Assert.True(await db.TimeSeriesCreateAsync(key, labels: labels)); 42 | 43 | var info = await db.TimeSeriesInfoAsync(key); 44 | Assert.Equal(labels, info.Labels); 45 | } 46 | 47 | [Fact] 48 | public async Task TestCreateEmptyLabels() 49 | { 50 | var key = CreateKeyName(); 51 | var labels = new List(); 52 | var db = redisFixture.Redis.GetDatabase(); 53 | Assert.True(await db.TimeSeriesCreateAsync(key, labels: labels)); 54 | 55 | var info = await db.TimeSeriesInfoAsync(key); 56 | Assert.Equal(labels, info.Labels); 57 | } 58 | 59 | [Fact] 60 | public async Task TestCreateUncompressed() 61 | { 62 | var key = CreateKeyName(); 63 | var db = redisFixture.Redis.GetDatabase(); 64 | Assert.True(await db.TimeSeriesCreateAsync(key, uncompressed: true)); 65 | } 66 | 67 | [Fact] 68 | public async void TestCreatehDuplicatePolicyFirst() 69 | { 70 | var key = CreateKeyName(); 71 | var db = redisFixture.Redis.GetDatabase(); 72 | Assert.True(await db.TimeSeriesCreateAsync(key, duplicatePolicy: TsDuplicatePolicy.FIRST)); 73 | } 74 | 75 | [Fact] 76 | public async void TestCreatehDuplicatePolicyLast() 77 | { 78 | var key = CreateKeyName(); 79 | var db = redisFixture.Redis.GetDatabase(); 80 | Assert.True(await db.TimeSeriesCreateAsync(key, duplicatePolicy: TsDuplicatePolicy.LAST)); 81 | } 82 | 83 | [Fact] 84 | public async void TestCreatehDuplicatePolicyMin() 85 | { 86 | var key = CreateKeyName(); 87 | var db = redisFixture.Redis.GetDatabase(); 88 | Assert.True(await db.TimeSeriesCreateAsync(key, duplicatePolicy: TsDuplicatePolicy.MIN)); 89 | } 90 | 91 | [Fact] 92 | public async void TestCreatehDuplicatePolicyMax() 93 | { 94 | var key = CreateKeyName(); 95 | var db = redisFixture.Redis.GetDatabase(); 96 | Assert.True(await db.TimeSeriesCreateAsync(key, duplicatePolicy: TsDuplicatePolicy.MAX)); 97 | } 98 | 99 | [Fact] 100 | public async void TestCreatehDuplicatePolicySum() 101 | { 102 | var key = CreateKeyName(); 103 | var db = redisFixture.Redis.GetDatabase(); 104 | Assert.True(await db.TimeSeriesCreateAsync(key, duplicatePolicy: TsDuplicatePolicy.SUM)); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /NRedisTimeSeries/DataTypes/TimeSeriesInformation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | using System.Collections.Generic; 4 | using NRedisTimeSeries.Commands.Enums; 5 | 6 | namespace NRedisTimeSeries.DataTypes 7 | { 8 | /// 9 | /// This class represents the response for TS.INFO command. 10 | /// This object has Read-only properties and cannot be generated outside a TS.INFO response. 11 | /// 12 | public class TimeSeriesInformation 13 | { 14 | /// 15 | /// Total samples in the time-series. 16 | /// 17 | public long TotalSamples { get; private set; } 18 | 19 | /// 20 | /// Total number of bytes allocated for the time-series. 21 | /// 22 | public long MemoryUsage { get; private set; } 23 | 24 | /// 25 | /// First timestamp present in the time-series. 26 | /// 27 | public TimeStamp FirstTimeStamp { get; private set; } 28 | 29 | /// 30 | /// Last timestamp present in the time-series. 31 | /// 32 | public TimeStamp LastTimeStamp { get; private set; } 33 | 34 | /// 35 | /// Retention time, in milliseconds, for the time-series. 36 | /// 37 | public long RetentionTime { get; private set; } 38 | 39 | /// 40 | /// Number of Memory Chunks used for the time-series. 41 | /// 42 | public long ChunkCount { get; private set; } 43 | 44 | /// 45 | /// Maximum Number of samples per Memory Chunk. 46 | /// 47 | [ObsoleteAttribute("This method has been deprecated. Use ChunkSize instead.")] 48 | public long MaxSamplesPerChunk { get; private set; } 49 | 50 | /// 51 | /// Memory Chunk size in Bytes. 52 | /// 53 | public long ChunkSize { get; private set; } 54 | 55 | /// 56 | /// A readonly list of TimeSeriesLabel that represent metadata labels of the time-series. 57 | /// 58 | public IReadOnlyList Labels { get; private set; } 59 | 60 | /// 61 | /// Source key for the queries time series key. 62 | /// 63 | public string SourceKey { get; private set; } 64 | 65 | /// 66 | /// A readonly list of TimeSeriesRules that represent compaction Rules of the time-series. 67 | /// 68 | public IReadOnlyList Rules { get; private set; } 69 | 70 | /// 71 | /// The policy will define handling of duplicate samples. 72 | /// 73 | public TsDuplicatePolicy? DuplicatePolicy { get; private set; } 74 | 75 | internal TimeSeriesInformation(long totalSamples, long memoryUsage, TimeStamp firstTimeStamp, TimeStamp lastTimeStamp, long retentionTime, long chunkCount, long chunkSize, IReadOnlyList labels, string sourceKey, IReadOnlyList rules, TsDuplicatePolicy? policy) 76 | { 77 | TotalSamples = totalSamples; 78 | MemoryUsage = memoryUsage; 79 | FirstTimeStamp = firstTimeStamp; 80 | LastTimeStamp = lastTimeStamp; 81 | RetentionTime = retentionTime; 82 | ChunkCount = chunkCount; 83 | Labels = labels; 84 | SourceKey = sourceKey; 85 | Rules = rules; 86 | // backwards compatible with RedisTimeSeries < v1.4 87 | MaxSamplesPerChunk = chunkSize/16; 88 | ChunkSize = chunkSize; 89 | // configure what to do on duplicate sample > v1.4 90 | DuplicatePolicy = policy; 91 | } 92 | 93 | /// 94 | /// Implicit cast from TimeSeriesInformation to string. 95 | /// 96 | /// TimeSeriesInformation 97 | public static implicit operator string(TimeSeriesInformation info) => JsonSerializer.Serialize(info); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /NRedisTimeSeries/DataTypes/TimeStamp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace NRedisTimeSeries.DataTypes 5 | { 6 | /// 7 | /// A class represents timestamp. 8 | /// Value can be either primitive long, DateTime or one of the strings "-", "+", "*". 9 | /// 10 | public class TimeStamp 11 | { 12 | private static readonly string[] constants = { "-", "+", "*" }; 13 | 14 | /// 15 | /// TimeStamp value. 16 | /// 17 | public object Value { get; private set; } 18 | 19 | /// 20 | /// Build a TimeStamp from primitive long. 21 | /// 22 | /// long value 23 | public TimeStamp(long timestamp) => Value = timestamp; 24 | 25 | /// 26 | /// Build a TimeStamp from DateTime. 27 | /// 28 | /// DateTime value 29 | public TimeStamp(DateTime dateTime) => Value = dateTime.Ticks; 30 | 31 | /// 32 | /// Build a TimeStamp from one of the strings "-", "+", "*". 33 | /// If the string is none of the above a NotSupportedException is thrown. 34 | /// 35 | /// String value 36 | public TimeStamp(string timestamp) 37 | { 38 | if (Array.IndexOf(constants, timestamp) == -1) 39 | { 40 | throw new NotSupportedException(string.Format("The string {0} cannot be used", timestamp)); 41 | } 42 | Value = timestamp; 43 | } 44 | 45 | /// 46 | /// Implicit cast from long to TimeStamp. 47 | /// 48 | /// long value. 49 | public static implicit operator TimeStamp(long l) => new TimeStamp(l); 50 | 51 | /// 52 | /// Implicit cast from TimeStamp to long. 53 | /// If the underlying timestamp value is not long or DateTime, an InvalidCastException is thrown. 54 | /// 55 | /// TimeStamp 56 | public static implicit operator long(TimeStamp ts) => 57 | ts.Value is long value ? value : throw new InvalidCastException("Cannot convert string timestamp to long"); 58 | 59 | /// 60 | /// Implicit cast from string to TimeStamp. 61 | /// Calls the string C'tor. 62 | /// 63 | /// String value 64 | public static implicit operator TimeStamp(string s) => new TimeStamp(s); 65 | 66 | /// 67 | /// Implicit cast from TimeStamp to string. 68 | /// 69 | /// TimeStamp 70 | public static implicit operator string(TimeStamp ts) => ts.Value.ToString(); 71 | 72 | /// 73 | /// Implicit cast from DateTime to TimeStamp. 74 | /// 75 | /// DateTime value 76 | public static implicit operator TimeStamp(DateTime dateTime) => new TimeStamp(dateTime); 77 | 78 | /// 79 | /// Implicit cast from TimeStamp to DateTime. 80 | /// 81 | /// TimeStamp 82 | public static implicit operator DateTime(TimeStamp timeStamp) => new DateTime(timeStamp); 83 | 84 | /// 85 | /// Equality of TimeSeriesTuple objects 86 | /// 87 | /// Object to compare 88 | /// If two TimeStamp objects are equal 89 | public override bool Equals(object obj) => 90 | obj is TimeStamp stamp && EqualityComparer.Default.Equals(Value, stamp.Value); 91 | 92 | /// 93 | /// TimeStamp object hash code. 94 | /// 95 | /// TimeStamp object hash code. 96 | public override int GetHashCode() => 97 | -1937169414 + EqualityComparer.Default.GetHashCode(Value); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/RangeExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.Commands.Enums; 4 | using NRedisTimeSeries.DataTypes; 5 | using StackExchange.Redis; 6 | 7 | namespace NRedisTimeSeries.Example 8 | { 9 | /// 10 | /// Examples for NRedisTimeSeries API for RANGE queries. 11 | /// 12 | public class RangeExample 13 | { 14 | /// 15 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis. 16 | /// NRedisTimeSeris Range is expecting two TimeStamps objects as the range boundries. 17 | /// In this case, the strings are implicitly casted into TimeStamp objects. 18 | /// The TimeSeriesRange command returns an IReadOnlyList collection. 19 | /// 20 | public static void DefaultRangeExample() 21 | { 22 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 23 | IDatabase db = redis.GetDatabase(); 24 | IReadOnlyList results = db.TimeSeriesRange("my_ts", "-", "+"); 25 | foreach(TimeSeriesTuple res in results) { 26 | Console.WriteLine(res); 27 | } 28 | redis.Close(); 29 | } 30 | 31 | /// 32 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis, and the COUNT parameter. 33 | /// NRedisTimeSeris Range is expecting two TimeStamps objects as the range boundries. 34 | /// In this case, the strings are implicitly casted into TimeStamp objects. 35 | /// The TimeSeriesRange command returns an IReadOnlyList collection. 36 | /// 37 | public static void CountRangeExample() 38 | { 39 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 40 | IDatabase db = redis.GetDatabase(); 41 | IReadOnlyList results = db.TimeSeriesRange("my_ts", "-", "+", count:50); 42 | foreach(TimeSeriesTuple res in results) { 43 | Console.WriteLine(res); 44 | } 45 | redis.Close(); 46 | } 47 | 48 | /// 49 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis, and MIN aggregation. 50 | /// NRedisTimeSeris Range is expecting two TimeStamps objects as the range boundries. 51 | /// In this case, the strings are implicitly casted into TimeStamp objects. 52 | /// The TimeSeriesRange command returns an IReadOnlyList collection. 53 | /// 54 | public static void RangeAggregationExample() 55 | { 56 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 57 | IDatabase db = redis.GetDatabase(); 58 | IReadOnlyList results = db.TimeSeriesRange("my_ts", "-", "+", aggregation: TsAggregation.Min, timeBucket: 50); 59 | foreach(TimeSeriesTuple res in results) { 60 | Console.WriteLine(res); 61 | } 62 | redis.Close(); 63 | } 64 | 65 | /// 66 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis, FILTER_BY_TS, and FILTER_BY_VALUE. 67 | /// NRedisTimeSeris Range is expecting two TimeStamps objects as the range boundries. 68 | /// In this case, the strings are implicitly casted into TimeStamp objects. 69 | /// The TimeSeriesRange command returns an IReadOnlyList collection. 70 | /// 71 | public static void RangeFilterByExample() 72 | { 73 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 74 | IDatabase db = redis.GetDatabase(); 75 | var filterTs = new List {0, 50, 100}; 76 | IReadOnlyList results = db.TimeSeriesRange("my_ts", "-", "+", filterByTs: filterTs, filterByValue: (2, 5)); 77 | foreach(TimeSeriesTuple res in results) { 78 | Console.WriteLine(res); 79 | } 80 | redis.Close(); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestIncrByAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using StackExchange.Redis; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | 8 | namespace NRedisTimeSeries.Test.TestAPI 9 | { 10 | public class TestIncrByAsync : AbstractTimeSeriesTest 11 | { 12 | public TestIncrByAsync(RedisFixture redisFixture) : base(redisFixture) { } 13 | 14 | [Fact] 15 | public async Task TestDefaultIncrBy() 16 | { 17 | var key = CreateKeyName(); 18 | var value = 5.5; 19 | var db = redisFixture.Redis.GetDatabase(); 20 | Assert.True(await db.TimeSeriesIncrByAsync(key, value) > 0); 21 | 22 | var result = await db.TimeSeriesGetAsync(key); 23 | Assert.Equal(value, result.Val); 24 | } 25 | 26 | [Fact] 27 | public async Task TestStarIncrBy() 28 | { 29 | var key = CreateKeyName(); 30 | var value = 5.5; 31 | var db = redisFixture.Redis.GetDatabase(); 32 | Assert.True(await db.TimeSeriesIncrByAsync(key, value, timestamp: "*") > 0); 33 | 34 | var result = await db.TimeSeriesGetAsync(key); 35 | Assert.Equal(value, result.Val); 36 | } 37 | 38 | [Fact] 39 | public async Task TestIncrByTimeStamp() 40 | { 41 | var key = CreateKeyName(); 42 | var value = 5.5; 43 | var db = redisFixture.Redis.GetDatabase(); 44 | TimeStamp timeStamp = DateTime.UtcNow; 45 | Assert.Equal(timeStamp, await db.TimeSeriesIncrByAsync(key, value, timestamp: timeStamp)); 46 | Assert.Equal(new TimeSeriesTuple(timeStamp, value), await db.TimeSeriesGetAsync(key)); 47 | } 48 | 49 | [Fact] 50 | public async Task TestDefaultIncrByWithRetentionTime() 51 | { 52 | var key = CreateKeyName(); 53 | var value = 5.5; 54 | long retentionTime = 5000; 55 | var db = redisFixture.Redis.GetDatabase(); 56 | Assert.True(await db.TimeSeriesIncrByAsync(key, value, retentionTime: retentionTime) > 0); 57 | 58 | var result = await db.TimeSeriesGetAsync(key); 59 | Assert.Equal(value, result.Val); 60 | 61 | var info = await db.TimeSeriesInfoAsync(key); 62 | Assert.Equal(retentionTime, info.RetentionTime); 63 | } 64 | 65 | [Fact] 66 | public async Task TestDefaultIncrByWithLabels() 67 | { 68 | var key = CreateKeyName(); 69 | var value = 5.5; 70 | var label = new TimeSeriesLabel("key", "value"); 71 | var db = redisFixture.Redis.GetDatabase(); 72 | var labels = new List { label }; 73 | Assert.True(await db.TimeSeriesIncrByAsync(key, value, labels: labels) > 0); 74 | 75 | var result = await db.TimeSeriesGetAsync(key); 76 | Assert.Equal(value, result.Val); 77 | 78 | var info = await db.TimeSeriesInfoAsync(key); 79 | Assert.Equal(labels, info.Labels); 80 | } 81 | 82 | [Fact] 83 | public async Task TestDefaultIncrByWithUncompressed() 84 | { 85 | var key = CreateKeyName(); 86 | var value = 5.5; 87 | var db = redisFixture.Redis.GetDatabase(); 88 | Assert.True(await db.TimeSeriesIncrByAsync(key, value, uncompressed: true) > 0); 89 | 90 | var result = await db.TimeSeriesGetAsync(key); 91 | Assert.Equal(value, result.Val); 92 | } 93 | 94 | [Fact] 95 | public async Task TestWrongParameters() 96 | { 97 | var key = CreateKeyName(); 98 | var value = 5.5; 99 | var db = redisFixture.Redis.GetDatabase(); 100 | var ex = await Assert.ThrowsAsync(async () => await db.TimeSeriesIncrByAsync(key, value, timestamp: "+")); 101 | Assert.Equal("ERR TSDB: invalid timestamp", ex.Message); 102 | 103 | ex = await Assert.ThrowsAsync(async () => await db.TimeSeriesIncrByAsync(key, value, timestamp: "-")); 104 | Assert.Equal("ERR TSDB: invalid timestamp", ex.Message); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /NRedisTimeSeries.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NRedisTimeSeries", "NRedisTimeSeries\NRedisTimeSeries.csproj", "{DE3DB9C6-D6CD-4F09-AC2E-5F3C871F0F4F}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NRedisTimeSeries.Test", "NRedisTimeSeries.Test\NRedisTimeSeries.Test.csproj", "{24A6A833-3677-4EBC-8A4D-94F50BFE3F38}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NRedisTimeSeries.Example", "NRedisTimeSeries.Example\NRedisTimeSeries.Example.csproj", "{CCA3729A-3834-4A65-B56A-EA36882B33EC}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Debug|x64 = Debug|x64 16 | Debug|x86 = Debug|x86 17 | Release|Any CPU = Release|Any CPU 18 | Release|x64 = Release|x64 19 | Release|x86 = Release|x86 20 | EndGlobalSection 21 | GlobalSection(SolutionProperties) = preSolution 22 | HideSolutionNode = FALSE 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {DE3DB9C6-D6CD-4F09-AC2E-5F3C871F0F4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {DE3DB9C6-D6CD-4F09-AC2E-5F3C871F0F4F}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {DE3DB9C6-D6CD-4F09-AC2E-5F3C871F0F4F}.Debug|x64.ActiveCfg = Debug|Any CPU 28 | {DE3DB9C6-D6CD-4F09-AC2E-5F3C871F0F4F}.Debug|x64.Build.0 = Debug|Any CPU 29 | {DE3DB9C6-D6CD-4F09-AC2E-5F3C871F0F4F}.Debug|x86.ActiveCfg = Debug|Any CPU 30 | {DE3DB9C6-D6CD-4F09-AC2E-5F3C871F0F4F}.Debug|x86.Build.0 = Debug|Any CPU 31 | {DE3DB9C6-D6CD-4F09-AC2E-5F3C871F0F4F}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {DE3DB9C6-D6CD-4F09-AC2E-5F3C871F0F4F}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {DE3DB9C6-D6CD-4F09-AC2E-5F3C871F0F4F}.Release|x64.ActiveCfg = Release|Any CPU 34 | {DE3DB9C6-D6CD-4F09-AC2E-5F3C871F0F4F}.Release|x64.Build.0 = Release|Any CPU 35 | {DE3DB9C6-D6CD-4F09-AC2E-5F3C871F0F4F}.Release|x86.ActiveCfg = Release|Any CPU 36 | {DE3DB9C6-D6CD-4F09-AC2E-5F3C871F0F4F}.Release|x86.Build.0 = Release|Any CPU 37 | {24A6A833-3677-4EBC-8A4D-94F50BFE3F38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {24A6A833-3677-4EBC-8A4D-94F50BFE3F38}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {24A6A833-3677-4EBC-8A4D-94F50BFE3F38}.Debug|x64.ActiveCfg = Debug|Any CPU 40 | {24A6A833-3677-4EBC-8A4D-94F50BFE3F38}.Debug|x64.Build.0 = Debug|Any CPU 41 | {24A6A833-3677-4EBC-8A4D-94F50BFE3F38}.Debug|x86.ActiveCfg = Debug|Any CPU 42 | {24A6A833-3677-4EBC-8A4D-94F50BFE3F38}.Debug|x86.Build.0 = Debug|Any CPU 43 | {24A6A833-3677-4EBC-8A4D-94F50BFE3F38}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {24A6A833-3677-4EBC-8A4D-94F50BFE3F38}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {24A6A833-3677-4EBC-8A4D-94F50BFE3F38}.Release|x64.ActiveCfg = Release|Any CPU 46 | {24A6A833-3677-4EBC-8A4D-94F50BFE3F38}.Release|x64.Build.0 = Release|Any CPU 47 | {24A6A833-3677-4EBC-8A4D-94F50BFE3F38}.Release|x86.ActiveCfg = Release|Any CPU 48 | {24A6A833-3677-4EBC-8A4D-94F50BFE3F38}.Release|x86.Build.0 = Release|Any CPU 49 | {CCA3729A-3834-4A65-B56A-EA36882B33EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {CCA3729A-3834-4A65-B56A-EA36882B33EC}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {CCA3729A-3834-4A65-B56A-EA36882B33EC}.Debug|x64.ActiveCfg = Debug|Any CPU 52 | {CCA3729A-3834-4A65-B56A-EA36882B33EC}.Debug|x64.Build.0 = Debug|Any CPU 53 | {CCA3729A-3834-4A65-B56A-EA36882B33EC}.Debug|x86.ActiveCfg = Debug|Any CPU 54 | {CCA3729A-3834-4A65-B56A-EA36882B33EC}.Debug|x86.Build.0 = Debug|Any CPU 55 | {CCA3729A-3834-4A65-B56A-EA36882B33EC}.Release|Any CPU.ActiveCfg = Release|Any CPU 56 | {CCA3729A-3834-4A65-B56A-EA36882B33EC}.Release|Any CPU.Build.0 = Release|Any CPU 57 | {CCA3729A-3834-4A65-B56A-EA36882B33EC}.Release|x64.ActiveCfg = Release|Any CPU 58 | {CCA3729A-3834-4A65-B56A-EA36882B33EC}.Release|x64.Build.0 = Release|Any CPU 59 | {CCA3729A-3834-4A65-B56A-EA36882B33EC}.Release|x86.ActiveCfg = Release|Any CPU 60 | {CCA3729A-3834-4A65-B56A-EA36882B33EC}.Release|x86.Build.0 = Release|Any CPU 61 | EndGlobalSection 62 | GlobalSection(MonoDevelopProperties) = preSolution 63 | version = 1.4.0 64 | Policies = $0 65 | $0.DotNetNamingPolicy = $1 66 | $1.DirectoryNamespaceAssociation = PrefixedHierarchical 67 | EndGlobalSection 68 | EndGlobal 69 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestDecrByAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.DataTypes; 2 | using StackExchange.Redis; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | 8 | namespace NRedisTimeSeries.Test.TestAPI 9 | { 10 | public class TestDecrByAsync : AbstractTimeSeriesTest 11 | { 12 | public TestDecrByAsync(RedisFixture redisFixture) : base(redisFixture) { } 13 | 14 | [Fact] 15 | public async Task TestDefaultDecrBy() 16 | { 17 | var key = CreateKeyName(); 18 | var value = 5.5; 19 | var db = redisFixture.Redis.GetDatabase(); 20 | Assert.True(await db.TimeSeriesDecrByAsync(key, -value) > 0); 21 | 22 | var result = await db.TimeSeriesGetAsync(key); 23 | Assert.Equal(value, result.Val); 24 | } 25 | 26 | [Fact] 27 | public async Task TestStarDecrBy() 28 | { 29 | var key = CreateKeyName(); 30 | var value = 5.5; 31 | var db = redisFixture.Redis.GetDatabase(); 32 | Assert.True(await db.TimeSeriesDecrByAsync(key, -value, timestamp: "*") > 0); 33 | 34 | var result = await db.TimeSeriesGetAsync(key); 35 | Assert.Equal(value, result.Val); 36 | } 37 | 38 | [Fact] 39 | public async Task TestDecrByTimeStamp() 40 | { 41 | var key = CreateKeyName(); 42 | var value = 5.5; 43 | var db = redisFixture.Redis.GetDatabase(); 44 | TimeStamp timeStamp = DateTime.UtcNow; 45 | Assert.Equal(timeStamp, await db.TimeSeriesDecrByAsync(key, -value, timestamp: timeStamp)); 46 | Assert.Equal(new TimeSeriesTuple(timeStamp, value), await db.TimeSeriesGetAsync(key)); 47 | } 48 | 49 | [Fact] 50 | public async Task TestDefaultDecrByWithRetentionTime() 51 | { 52 | var key = CreateKeyName(); 53 | var value = 5.5; 54 | long retentionTime = 5000; 55 | var db = redisFixture.Redis.GetDatabase(); 56 | Assert.True(await db.TimeSeriesDecrByAsync(key, -value, retentionTime: retentionTime) > 0); 57 | 58 | var result = await db.TimeSeriesGetAsync(key); 59 | Assert.Equal(value, result.Val); 60 | 61 | var info = await db.TimeSeriesInfoAsync(key); 62 | Assert.Equal(retentionTime, info.RetentionTime); 63 | } 64 | 65 | [Fact] 66 | public async Task TestDefaultDecrByWithLabels() 67 | { 68 | var key = CreateKeyName(); 69 | var value = 5.5; 70 | var label = new TimeSeriesLabel("key", "value"); 71 | var db = redisFixture.Redis.GetDatabase(); 72 | var labels = new List { label }; 73 | Assert.True(await db.TimeSeriesDecrByAsync(key, -value, labels: labels) > 0); 74 | 75 | var result = await db.TimeSeriesGetAsync(key); 76 | Assert.Equal(value, result.Val); 77 | 78 | var info = await db.TimeSeriesInfoAsync(key); 79 | Assert.Equal(labels, info.Labels); 80 | } 81 | 82 | [Fact] 83 | public async Task TestDefaultDecrByWithUncompressed() 84 | { 85 | var key = CreateKeyName(); 86 | var value = 5.5; 87 | var db = redisFixture.Redis.GetDatabase(); 88 | Assert.True(await db.TimeSeriesDecrByAsync(key, -value, uncompressed: true) > 0); 89 | 90 | var result = await db.TimeSeriesGetAsync(key); 91 | Assert.Equal(value, result.Val); 92 | } 93 | 94 | [Fact] 95 | public async Task TestWrongParameters() 96 | { 97 | var key = CreateKeyName(); 98 | var value = 5.5; 99 | var db = redisFixture.Redis.GetDatabase(); 100 | var ex = await Assert.ThrowsAsync(async () => await db.TimeSeriesDecrByAsync(key, value, timestamp: "+")); 101 | Assert.Equal("ERR TSDB: invalid timestamp", ex.Message); 102 | 103 | ex = await Assert.ThrowsAsync(async () => await db.TimeSeriesDecrByAsync(key, value, timestamp: "-")); 104 | Assert.Equal("ERR TSDB: invalid timestamp", ex.Message); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/RangeExampleAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.Commands.Enums; 2 | using NRedisTimeSeries.DataTypes; 3 | using StackExchange.Redis; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | namespace NRedisTimeSeries.Example 9 | { 10 | /// 11 | /// Examples for NRedisTimeSeries async API for RANGE queries. 12 | /// 13 | public class RangeAsyncExample 14 | { 15 | /// 16 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis. 17 | /// NRedisTimeSeris Range is expecting two TimeStamps objects as the range boundries. 18 | /// In this case, the strings are implicitly casted into TimeStamp objects. 19 | /// The TimeSeriesRange command returns an IReadOnlyList collection. 20 | /// 21 | public static async Task DefaultRangeAsyncExample() 22 | { 23 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 24 | IDatabase db = redis.GetDatabase(); 25 | IReadOnlyList results = await db.TimeSeriesRangeAsync("my_ts", "-", "+"); 26 | foreach(TimeSeriesTuple res in results) { 27 | Console.WriteLine(res); 28 | } 29 | redis.Close(); 30 | } 31 | 32 | /// 33 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis, and the COUNT parameter. 34 | /// NRedisTimeSeris Range is expecting two TimeStamps objects as the range boundries. 35 | /// In this case, the strings are implicitly casted into TimeStamp objects. 36 | /// The TimeSeriesRange command returns an IReadOnlyList collection. 37 | /// 38 | public static async Task CountRangeAsyncExample() 39 | { 40 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 41 | IDatabase db = redis.GetDatabase(); 42 | IReadOnlyList results = await db.TimeSeriesRangeAsync("my_ts", "-", "+", count:50); 43 | foreach(TimeSeriesTuple res in results) { 44 | Console.WriteLine(res); 45 | } 46 | redis.Close(); 47 | } 48 | 49 | /// 50 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis, and MIN aggregation. 51 | /// NRedisTimeSeris Range is expecting two TimeStamps objects as the range boundries. 52 | /// In this case, the strings are implicitly casted into TimeStamp objects. 53 | /// The TimeSeriesRange command returns an IReadOnlyList collection. 54 | /// 55 | public static async Task RangeAggregationAsyncExample() 56 | { 57 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 58 | IDatabase db = redis.GetDatabase(); 59 | IReadOnlyList results = await db.TimeSeriesRangeAsync("my_ts", "-", "+", aggregation: TsAggregation.Min, timeBucket: 50); 60 | foreach(TimeSeriesTuple res in results) { 61 | Console.WriteLine(res); 62 | } 63 | redis.Close(); 64 | } 65 | 66 | /// 67 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis, FILTER_BY_TS, and FILTER_BY_VALUE. 68 | /// NRedisTimeSeris Range is expecting two TimeStamps objects as the range boundries. 69 | /// In this case, the strings are implicitly casted into TimeStamp objects. 70 | /// The TimeSeriesRange command returns an IReadOnlyList collection. 71 | /// 72 | public static async Task RangeFilterByExample() 73 | { 74 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 75 | IDatabase db = redis.GetDatabase(); 76 | var filterTs = new List {0, 50, 100}; 77 | IReadOnlyList results = await db.TimeSeriesRangeAsync("my_ts", "-", "+", filterByTs: filterTs, filterByValue: (2, 5)); 78 | foreach(TimeSeriesTuple res in results) { 79 | Console.WriteLine(res); 80 | } 81 | redis.Close(); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestRules.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.Commands.Enums; 4 | using NRedisTimeSeries.DataTypes; 5 | using StackExchange.Redis; 6 | using Xunit; 7 | 8 | namespace NRedisTimeSeries.Test.TestAPI 9 | { 10 | public class TestRules : AbstractTimeSeriesTest, IDisposable 11 | { 12 | private string srcKey = "RULES_TEST_SRC"; 13 | 14 | private Dictionary destKeys; 15 | 16 | public TestRules(RedisFixture redisFixture) : base(redisFixture) 17 | { 18 | 19 | destKeys = new Dictionary 20 | { 21 | { TsAggregation.Avg, "RULES_DEST_" + TsAggregation.Avg }, 22 | { TsAggregation.Count, "RULES_DEST_" + TsAggregation.Count }, 23 | { TsAggregation.First, "RULES_DEST_" + TsAggregation.First }, 24 | { TsAggregation.Last, "RULES_DEST_" + TsAggregation.Last }, 25 | { TsAggregation.Max, "RULES_DEST_" + TsAggregation.Max }, 26 | { TsAggregation.Min, "RULES_DEST_" + TsAggregation.Min }, 27 | { TsAggregation.Range, "RULES_DEST_" + TsAggregation.Range }, 28 | { TsAggregation.StdP, "RULES_DEST_" + TsAggregation.StdP }, 29 | { TsAggregation.StdS, "RULES_DEST_" + TsAggregation.StdS }, 30 | { TsAggregation.Sum, "RULES_DEST_" + TsAggregation.Sum }, 31 | { TsAggregation.VarP, "RULES_DEST_" + TsAggregation.VarP }, 32 | { TsAggregation.VarS, "RULES_DEST_" + TsAggregation.VarS } 33 | }; 34 | } 35 | 36 | public void Dispose() 37 | { 38 | redisFixture.Redis.GetDatabase().KeyDelete(srcKey); 39 | foreach(var key in destKeys.Values) 40 | { 41 | redisFixture.Redis.GetDatabase().KeyDelete(key); 42 | } 43 | } 44 | 45 | [Fact] 46 | public void TestRulesAdditionDeletion() 47 | { 48 | IDatabase db = redisFixture.Redis.GetDatabase(); 49 | db.TimeSeriesCreate(srcKey); 50 | foreach(var destKey in destKeys.Values) 51 | { 52 | db.TimeSeriesCreate(destKey); 53 | } 54 | long timeBucket = 50; 55 | var rules = new List(); 56 | var rulesMap = new Dictionary(); 57 | foreach(var aggregation in destKeys.Keys) 58 | { 59 | var rule = new TimeSeriesRule(destKeys[aggregation], timeBucket, aggregation); 60 | rules.Add(rule); 61 | rulesMap[aggregation] = rule; 62 | Assert.True(db.TimeSeriesCreateRule(srcKey, rule)); 63 | TimeSeriesInformation info = db.TimeSeriesInfo(srcKey); 64 | Assert.Equal(rules, info.Rules); 65 | } 66 | foreach(var aggregation in destKeys.Keys) 67 | { 68 | var rule = rulesMap[aggregation]; 69 | rules.Remove(rule); 70 | Assert.True(db.TimeSeriesDeleteRule(srcKey, rule.DestKey)); 71 | TimeSeriesInformation info = db.TimeSeriesInfo(srcKey); 72 | Assert.Equal(rules, info.Rules); 73 | } 74 | } 75 | 76 | [Fact] 77 | public void TestNonExistingSrc() 78 | { 79 | IDatabase db = redisFixture.Redis.GetDatabase(); 80 | string destKey = "RULES_DEST_" + TsAggregation.Avg; 81 | db.TimeSeriesCreate(destKey); 82 | TimeSeriesRule rule = new TimeSeriesRule(destKey, 50, TsAggregation.Avg); 83 | var ex = Assert.Throws(() => db.TimeSeriesCreateRule(srcKey, rule)); 84 | Assert.Equal("ERR TSDB: the key does not exist", ex.Message); 85 | ex = Assert.Throws(() => db.TimeSeriesDeleteRule(srcKey, destKey)); 86 | Assert.Equal("ERR TSDB: the key does not exist", ex.Message); 87 | } 88 | 89 | [Fact] 90 | public void TestNonExisitingDestinaion() 91 | { 92 | IDatabase db = redisFixture.Redis.GetDatabase(); 93 | string destKey = "RULES_DEST_" + TsAggregation.Avg; 94 | db.TimeSeriesCreate(srcKey); 95 | TimeSeriesRule rule = new TimeSeriesRule(destKey, 50, TsAggregation.Avg); 96 | var ex = Assert.Throws(() => db.TimeSeriesCreateRule(srcKey, rule)); 97 | Assert.Equal("ERR TSDB: the key does not exist", ex.Message); 98 | ex = Assert.Throws(() => db.TimeSeriesDeleteRule(srcKey, destKey)); 99 | Assert.Equal("ERR TSDB: compaction rule does not exist", ex.Message); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestRange.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.Commands.Enums; 4 | using NRedisTimeSeries.DataTypes; 5 | using StackExchange.Redis; 6 | using Xunit; 7 | 8 | namespace NRedisTimeSeries.Test.TestAPI 9 | { 10 | public class TestRange : AbstractTimeSeriesTest, IDisposable 11 | { 12 | private readonly string key = "RANGE_TESTS"; 13 | 14 | public TestRange(RedisFixture redisFixture) : base(redisFixture) { } 15 | 16 | public void Dispose() 17 | { 18 | redisFixture.Redis.GetDatabase().KeyDelete(key); 19 | } 20 | 21 | private List CreateData(IDatabase db, int timeBucket) 22 | { 23 | var tuples = new List(); 24 | for (int i = 0; i < 10; i++) 25 | { 26 | TimeStamp ts = db.TimeSeriesAdd(key, i*timeBucket, i); 27 | tuples.Add(new TimeSeriesTuple(ts, i)); 28 | } 29 | return tuples; 30 | } 31 | 32 | [Fact] 33 | public void TestSimpleRange() 34 | { 35 | IDatabase db = redisFixture.Redis.GetDatabase(); 36 | var tuples = CreateData(db, 50); 37 | Assert.Equal(tuples, db.TimeSeriesRange(key, "-", "+")); 38 | } 39 | 40 | [Fact] 41 | public void TestRangeCount() 42 | { 43 | IDatabase db = redisFixture.Redis.GetDatabase(); 44 | var tuples = CreateData(db, 50); 45 | Assert.Equal(tuples.GetRange(0, 5), db.TimeSeriesRange(key, "-", "+", count: 5)); 46 | } 47 | 48 | [Fact] 49 | public void TestRangeAggregation() 50 | { 51 | IDatabase db = redisFixture.Redis.GetDatabase(); 52 | var tuples = CreateData(db, 50); 53 | Assert.Equal(tuples, db.TimeSeriesRange(key, "-", "+", aggregation: TsAggregation.Min, timeBucket: 50)); 54 | } 55 | 56 | [Fact] 57 | public void TestRangeAlign() 58 | { 59 | IDatabase db = redisFixture.Redis.GetDatabase(); 60 | var tuples = new List() 61 | { 62 | new TimeSeriesTuple(1, 10), 63 | new TimeSeriesTuple(3, 5), 64 | new TimeSeriesTuple(11, 10), 65 | new TimeSeriesTuple(21, 11) 66 | }; 67 | 68 | foreach (var tuple in tuples) 69 | { 70 | db.TimeSeriesAdd(key, tuple.Time, tuple.Val); 71 | } 72 | 73 | // Aligh start 74 | var resStart = new List() 75 | { 76 | new TimeSeriesTuple(1, 2), 77 | new TimeSeriesTuple(11, 1), 78 | new TimeSeriesTuple(21, 1) 79 | }; 80 | Assert.Equal(resStart, db.TimeSeriesRange(key, 1, 30, align: "-", aggregation: TsAggregation.Count, timeBucket: 10)); 81 | 82 | // Aligh end 83 | var resEnd = new List() 84 | { 85 | new TimeSeriesTuple(0, 2), 86 | new TimeSeriesTuple(10, 1), 87 | new TimeSeriesTuple(20, 1) 88 | }; 89 | Assert.Equal(resEnd, db.TimeSeriesRange(key, 1, 30, align: "+", aggregation: TsAggregation.Count, timeBucket: 10)); 90 | 91 | // Align 1 92 | Assert.Equal(resStart, db.TimeSeriesRange(key, 1, 30, align: 1, aggregation: TsAggregation.Count, timeBucket: 10)); 93 | } 94 | 95 | [Fact] 96 | public void TestMissingTimeBucket() 97 | { 98 | IDatabase db = redisFixture.Redis.GetDatabase(); 99 | var tuples = CreateData(db, 50); 100 | var ex = Assert.Throws(() => db.TimeSeriesRange(key, "-", "+", aggregation: TsAggregation.Avg)); 101 | Assert.Equal("RANGE Aggregation should have timeBucket value", ex.Message); 102 | } 103 | 104 | [Fact] 105 | public void TestFilterBy() 106 | { 107 | IDatabase db = redisFixture.Redis.GetDatabase(); 108 | var tuples = CreateData(db, 50); 109 | 110 | var res = db.TimeSeriesRange(key, "-", "+", filterByValue: (0,2)); // The first 3 tuples 111 | Assert.Equal(3, res.Count); 112 | Assert.Equal(tuples.GetRange(0,3), res); 113 | 114 | var filterTs = new List {0, 50, 100}; // Also the first 3 tuples 115 | res = db.TimeSeriesRange(key, "-", "+", filterByTs: filterTs); 116 | Assert.Equal(tuples.GetRange(0,3), res); 117 | 118 | res = db.TimeSeriesRange(key, "-", "+", filterByTs: filterTs, filterByValue: (2, 5)); // The third tuple 119 | Assert.Equal(tuples.GetRange(2,1), res); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestRevRange.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.Commands.Enums; 4 | using NRedisTimeSeries.DataTypes; 5 | using StackExchange.Redis; 6 | using Xunit; 7 | 8 | namespace NRedisTimeSeries.Test.TestAPI 9 | { 10 | public class TestRevRange : AbstractTimeSeriesTest 11 | { 12 | public TestRevRange(RedisFixture redisFixture) : base(redisFixture) { } 13 | 14 | private List CreateData(IDatabase db, string key, int timeBucket) 15 | { 16 | var tuples = new List(); 17 | for (var i = 0; i < 10; i++) 18 | { 19 | var ts = db.TimeSeriesAdd(key, i * timeBucket, i); 20 | tuples.Add(new TimeSeriesTuple(ts, i)); 21 | } 22 | return tuples; 23 | } 24 | 25 | [Fact] 26 | public void TestSimpleRevRange() 27 | { 28 | var key = CreateKeyName(); 29 | var db = redisFixture.Redis.GetDatabase(); 30 | var tuples = CreateData(db, key, 50); 31 | Assert.Equal(ReverseData(tuples), db.TimeSeriesRevRange(key, "-", "+")); 32 | } 33 | 34 | [Fact] 35 | public void TestRevRangeCount() 36 | { 37 | var key = CreateKeyName(); 38 | var db = redisFixture.Redis.GetDatabase(); 39 | var tuples = CreateData(db, key, 50); 40 | Assert.Equal(ReverseData(tuples).GetRange(0, 5), db.TimeSeriesRevRange(key, "-", "+", count: 5)); 41 | } 42 | 43 | [Fact] 44 | public void TestRevRangeAggregation() 45 | { 46 | var key = CreateKeyName(); 47 | var db = redisFixture.Redis.GetDatabase(); 48 | var tuples = CreateData(db, key, 50); 49 | Assert.Equal(ReverseData(tuples), db.TimeSeriesRevRange(key, "-", "+", aggregation: TsAggregation.Min, timeBucket: 50)); 50 | } 51 | 52 | [Fact] 53 | public void TestRevRangeAlign() 54 | { 55 | var key = CreateKeyName(); 56 | var db = redisFixture.Redis.GetDatabase(); 57 | var tuples = new List() 58 | { 59 | new TimeSeriesTuple(1, 10), 60 | new TimeSeriesTuple(3, 5), 61 | new TimeSeriesTuple(11, 10), 62 | new TimeSeriesTuple(21, 11) 63 | }; 64 | 65 | foreach (var tuple in tuples) 66 | { 67 | db.TimeSeriesAdd(key, tuple.Time, tuple.Val); 68 | } 69 | 70 | // Aligh start 71 | var resStart = new List() 72 | { 73 | new TimeSeriesTuple(21, 1), 74 | new TimeSeriesTuple(11, 1), 75 | new TimeSeriesTuple(1, 2) 76 | }; 77 | Assert.Equal(resStart, db.TimeSeriesRevRange(key, 1, 30, align: "-", aggregation: TsAggregation.Count, timeBucket: 10)); 78 | 79 | // Aligh end 80 | var resEnd = new List() 81 | { 82 | new TimeSeriesTuple(20, 1), 83 | new TimeSeriesTuple(10, 1), 84 | new TimeSeriesTuple(0, 2) 85 | }; 86 | Assert.Equal(resEnd, db.TimeSeriesRevRange(key, 1, 30, align: "+", aggregation: TsAggregation.Count, timeBucket: 10)); 87 | 88 | // Align 1 89 | Assert.Equal(resStart, db.TimeSeriesRevRange(key, 1, 30, align: 1, aggregation: TsAggregation.Count, timeBucket: 10)); 90 | } 91 | 92 | [Fact] 93 | public void TestMissingTimeBucket() 94 | { 95 | var key = CreateKeyName(); 96 | var db = redisFixture.Redis.GetDatabase(); 97 | var tuples = CreateData(db, key, 50); 98 | var ex = Assert.Throws(() => db.TimeSeriesRevRange(key, "-", "+", aggregation: TsAggregation.Avg)); 99 | Assert.Equal("RANGE Aggregation should have timeBucket value", ex.Message); 100 | 101 | } 102 | 103 | [Fact] 104 | public void TestFilterBy() 105 | { 106 | var key = CreateKeyName(); 107 | var db = redisFixture.Redis.GetDatabase(); 108 | var tuples = CreateData(db, key, 50); 109 | 110 | var res = db.TimeSeriesRevRange(key, "-", "+", filterByValue: (0,2)); 111 | Assert.Equal(3, res.Count); 112 | Assert.Equal(ReverseData(tuples.GetRange(0,3)), res); 113 | 114 | var filterTs = new List {0, 50, 100}; 115 | res = db.TimeSeriesRevRange(key, "-", "+", filterByTs: filterTs); 116 | Assert.Equal(ReverseData(tuples.GetRange(0,3)), res); 117 | 118 | res = db.TimeSeriesRevRange(key, "-", "+", filterByTs: filterTs, filterByValue: (2, 5)); 119 | Assert.Equal(tuples.GetRange(2,1), res); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestRangeAsync.cs: -------------------------------------------------------------------------------- 1 | using NRedisTimeSeries.Commands.Enums; 2 | using NRedisTimeSeries.DataTypes; 3 | using StackExchange.Redis; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | using Xunit; 8 | 9 | namespace NRedisTimeSeries.Test.TestAPI 10 | { 11 | public class TestRangeAsync : AbstractTimeSeriesTest 12 | { 13 | public TestRangeAsync(RedisFixture redisFixture) : base(redisFixture) { } 14 | 15 | private async Task> CreateData(IDatabase db, string key, int timeBucket) 16 | { 17 | var tuples = new List(); 18 | for (var i = 0; i < 10; i++) 19 | { 20 | var ts = await db.TimeSeriesAddAsync(key, i * timeBucket, i); 21 | tuples.Add(new TimeSeriesTuple(ts, i)); 22 | } 23 | return tuples; 24 | } 25 | 26 | [Fact] 27 | public async Task TestSimpleRange() 28 | { 29 | var key = CreateKeyName(); 30 | var db = redisFixture.Redis.GetDatabase(); 31 | var tuples = await CreateData(db, key, 50); 32 | Assert.Equal(tuples, await db.TimeSeriesRangeAsync(key, "-", "+")); 33 | } 34 | 35 | [Fact] 36 | public async Task TestRangeCount() 37 | { 38 | var key = CreateKeyName(); 39 | var db = redisFixture.Redis.GetDatabase(); 40 | var tuples = await CreateData(db, key, 50); 41 | Assert.Equal(tuples.GetRange(0, 5), await db.TimeSeriesRangeAsync(key, "-", "+", count: 5)); 42 | } 43 | 44 | [Fact] 45 | public async Task TestRangeAggregation() 46 | { 47 | var key = CreateKeyName(); 48 | var db = redisFixture.Redis.GetDatabase(); 49 | var tuples = await CreateData(db, key, 50); 50 | Assert.Equal(tuples, await db.TimeSeriesRangeAsync(key, "-", "+", aggregation: TsAggregation.Min, timeBucket: 50)); 51 | } 52 | 53 | [Fact] 54 | public async Task TestRangeAlign() 55 | { 56 | var key = CreateKeyName(); 57 | IDatabase db = redisFixture.Redis.GetDatabase(); 58 | var tuples = new List() 59 | { 60 | new TimeSeriesTuple(1, 10), 61 | new TimeSeriesTuple(3, 5), 62 | new TimeSeriesTuple(11, 10), 63 | new TimeSeriesTuple(21, 11) 64 | }; 65 | 66 | foreach (var tuple in tuples) 67 | { 68 | await db.TimeSeriesAddAsync(key, tuple.Time, tuple.Val); 69 | } 70 | 71 | // Aligh start 72 | var resStart = new List() 73 | { 74 | new TimeSeriesTuple(1, 2), 75 | new TimeSeriesTuple(11, 1), 76 | new TimeSeriesTuple(21, 1) 77 | }; 78 | Assert.Equal(resStart, await db.TimeSeriesRangeAsync(key, 1, 30, align: "-", aggregation: TsAggregation.Count, timeBucket: 10)); 79 | 80 | // Aligh end 81 | var resEnd = new List() 82 | { 83 | new TimeSeriesTuple(0, 2), 84 | new TimeSeriesTuple(10, 1), 85 | new TimeSeriesTuple(20, 1) 86 | }; 87 | Assert.Equal(resEnd, await db.TimeSeriesRangeAsync(key, 1, 30, align: "+", aggregation: TsAggregation.Count, timeBucket: 10)); 88 | 89 | // Align 1 90 | Assert.Equal(resStart, await db.TimeSeriesRangeAsync(key, 1, 30, align: 1, aggregation: TsAggregation.Count, timeBucket: 10)); 91 | } 92 | 93 | [Fact] 94 | public async Task TestMissingTimeBucket() 95 | { 96 | var key = CreateKeyName(); 97 | var db = redisFixture.Redis.GetDatabase(); 98 | var tuples = await CreateData(db, key, 50); 99 | var ex = await Assert.ThrowsAsync(async () => await db.TimeSeriesRangeAsync(key, "-", "+", aggregation: TsAggregation.Avg)); 100 | Assert.Equal("RANGE Aggregation should have timeBucket value", ex.Message); 101 | } 102 | 103 | [Fact] 104 | public async Task TestFilterBy() 105 | { 106 | var key = CreateKeyName(); 107 | var db = redisFixture.Redis.GetDatabase(); 108 | var tuples = await CreateData(db, key, 50); 109 | 110 | var res = await db.TimeSeriesRangeAsync(key, "-", "+", filterByValue: (0,2)); // The first 3 tuples 111 | Assert.Equal(3, res.Count); 112 | Assert.Equal(tuples.GetRange(0,3), res); 113 | 114 | var filterTs = new List {0, 50, 100}; // Also the first 3 tuples 115 | res = await db.TimeSeriesRangeAsync(key, "-", "+", filterByTs: filterTs); 116 | Assert.Equal(tuples.GetRange(0,3), res); 117 | 118 | res = await db.TimeSeriesRangeAsync(key, "-", "+", filterByTs: filterTs, filterByValue: (2, 5)); // The third tuple 119 | Assert.Equal(tuples.GetRange(2,1), res); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestRevRangeAsync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using StackExchange.Redis; 5 | using NRedisTimeSeries.Commands.Enums; 6 | using NRedisTimeSeries.DataTypes; 7 | using Xunit; 8 | 9 | namespace NRedisTimeSeries.Test.TestAPI 10 | { 11 | public class TestRevRangeAsync : AbstractTimeSeriesTest 12 | { 13 | public TestRevRangeAsync(RedisFixture redisFixture) : base(redisFixture) { } 14 | 15 | private async Task> CreateData(IDatabase db, string key, int timeBucket) 16 | { 17 | var tuples = new List(); 18 | for (var i = 0; i < 10; i++) 19 | { 20 | var ts = await db.TimeSeriesAddAsync(key, i * timeBucket, i); 21 | tuples.Add(new TimeSeriesTuple(ts, i)); 22 | } 23 | return tuples; 24 | } 25 | 26 | [Fact] 27 | public async Task TestSimpleRevRange() 28 | { 29 | var key = CreateKeyName(); 30 | var db = redisFixture.Redis.GetDatabase(); 31 | var tuples = await CreateData(db, key, 50); 32 | Assert.Equal(ReverseData(tuples), await db.TimeSeriesRevRangeAsync(key, "-", "+")); 33 | } 34 | 35 | [Fact] 36 | public async Task TestRevRangeCount() 37 | { 38 | var key = CreateKeyName(); 39 | var db = redisFixture.Redis.GetDatabase(); 40 | var tuples = await CreateData(db, key, 50); 41 | Assert.Equal(ReverseData(tuples).GetRange(0, 5), await db.TimeSeriesRevRangeAsync(key, "-", "+", count: 5)); 42 | } 43 | 44 | [Fact] 45 | public async Task TestRevRangeAggregation() 46 | { 47 | var key = CreateKeyName(); 48 | var db = redisFixture.Redis.GetDatabase(); 49 | var tuples = await CreateData(db, key, 50); 50 | Assert.Equal(ReverseData(tuples), await db.TimeSeriesRevRangeAsync(key, "-", "+", aggregation: TsAggregation.Min, timeBucket: 50)); 51 | } 52 | 53 | [Fact] 54 | public async Task TestRevRangeAlign() 55 | { 56 | var key = CreateKeyName(); 57 | IDatabase db = redisFixture.Redis.GetDatabase(); 58 | var tuples = new List() 59 | { 60 | new TimeSeriesTuple(1, 10), 61 | new TimeSeriesTuple(3, 5), 62 | new TimeSeriesTuple(11, 10), 63 | new TimeSeriesTuple(21, 11) 64 | }; 65 | 66 | foreach (var tuple in tuples) 67 | { 68 | await db.TimeSeriesAddAsync(key, tuple.Time, tuple.Val); 69 | } 70 | 71 | // Aligh start 72 | var resStart = new List() 73 | { 74 | new TimeSeriesTuple(21, 1), 75 | new TimeSeriesTuple(11, 1), 76 | new TimeSeriesTuple(1, 2) 77 | }; 78 | Assert.Equal(resStart, await db.TimeSeriesRevRangeAsync(key, 1, 30, align: "-", aggregation: TsAggregation.Count, timeBucket: 10)); 79 | 80 | // Aligh end 81 | var resEnd = new List() 82 | { 83 | new TimeSeriesTuple(20, 1), 84 | new TimeSeriesTuple(10, 1), 85 | new TimeSeriesTuple(0, 2) 86 | }; 87 | Assert.Equal(resEnd, await db.TimeSeriesRevRangeAsync(key, 1, 30, align: "+", aggregation: TsAggregation.Count, timeBucket: 10)); 88 | 89 | // Align 1 90 | Assert.Equal(resStart, await db.TimeSeriesRevRangeAsync(key, 1, 30, align: 1, aggregation: TsAggregation.Count, timeBucket: 10)); 91 | } 92 | 93 | [Fact] 94 | public async Task TestMissingTimeBucket() 95 | { 96 | var key = CreateKeyName(); 97 | var db = redisFixture.Redis.GetDatabase(); 98 | var tuples = await CreateData(db, key, 50); 99 | var ex = await Assert.ThrowsAsync(async () => await db.TimeSeriesRevRangeAsync(key, "-", "+", aggregation: TsAggregation.Avg)); 100 | Assert.Equal("RANGE Aggregation should have timeBucket value", ex.Message); 101 | } 102 | 103 | [Fact] 104 | public async Task TestFilterBy() 105 | { 106 | var key = CreateKeyName(); 107 | var db = redisFixture.Redis.GetDatabase(); 108 | var tuples = await CreateData(db, key, 50); 109 | 110 | var res = await db.TimeSeriesRevRangeAsync(key, "-", "+", filterByValue: (0,2)); 111 | Assert.Equal(3, res.Count); 112 | Assert.Equal(ReverseData(tuples.GetRange(0,3)), res); 113 | 114 | var filterTs = new List {0, 50, 100}; 115 | res = await db.TimeSeriesRevRangeAsync(key, "-", "+", filterByTs: filterTs); 116 | Assert.Equal(ReverseData(tuples.GetRange(0,3)), res); 117 | 118 | res = await db.TimeSeriesRevRangeAsync(key, "-", "+", filterByTs: filterTs, filterByValue: (2, 5)); 119 | Assert.Equal(tuples.GetRange(2,1), res); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Test/TestAPI/TestAdd.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using StackExchange.Redis; 5 | using NRedisTimeSeries.DataTypes; 6 | using NRedisTimeSeries.Commands; 7 | using NRedisTimeSeries.Commands.Enums; 8 | using Xunit; 9 | 10 | namespace NRedisTimeSeries.Test.TestAPI 11 | { 12 | public class TestAdd : AbstractTimeSeriesTest, IDisposable 13 | { 14 | private readonly string key = "ADD_TESTS"; 15 | 16 | public TestAdd(RedisFixture redisFixture) : base(redisFixture) { } 17 | 18 | public void Dispose() 19 | { 20 | redisFixture.Redis.GetDatabase().KeyDelete(key); 21 | } 22 | 23 | [Fact] 24 | public void TestAddNotExistingTimeSeries() 25 | { 26 | IDatabase db = redisFixture.Redis.GetDatabase(); 27 | TimeStamp now = DateTime.UtcNow; 28 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.1)); 29 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 30 | Assert.Equal(now, info.FirstTimeStamp); 31 | Assert.Equal(now, info.LastTimeStamp); 32 | } 33 | 34 | [Fact] 35 | public void TestAddExistingTimeSeries() 36 | { 37 | IDatabase db = redisFixture.Redis.GetDatabase(); 38 | db.TimeSeriesCreate(key); 39 | TimeStamp now = DateTime.UtcNow; 40 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.1)); 41 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 42 | Assert.Equal(now, info.FirstTimeStamp); 43 | Assert.Equal(now, info.LastTimeStamp); 44 | } 45 | 46 | [Fact] 47 | public void TestAddStar() 48 | { 49 | IDatabase db = redisFixture.Redis.GetDatabase(); 50 | db.TimeSeriesAdd(key, "*", 1.1); 51 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 52 | Assert.True(info.FirstTimeStamp > 0); 53 | Assert.Equal(info.FirstTimeStamp, info.LastTimeStamp); 54 | } 55 | 56 | [Fact] 57 | public void TestAddWithRetentionTime() 58 | { 59 | IDatabase db = redisFixture.Redis.GetDatabase(); 60 | TimeStamp now = DateTime.UtcNow; 61 | long retentionTime = 5000; 62 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.1, retentionTime: retentionTime)); 63 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 64 | Assert.Equal(now, info.FirstTimeStamp); 65 | Assert.Equal(now, info.LastTimeStamp); 66 | Assert.Equal(retentionTime, info.RetentionTime); 67 | } 68 | 69 | [Fact] 70 | public void TestAddWithLabels() 71 | { 72 | IDatabase db = redisFixture.Redis.GetDatabase(); 73 | TimeStamp now = DateTime.UtcNow; 74 | TimeSeriesLabel label = new TimeSeriesLabel("key", "value"); 75 | var labels = new List { label }; 76 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.1, labels: labels)); 77 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 78 | Assert.Equal(now, info.FirstTimeStamp); 79 | Assert.Equal(now, info.LastTimeStamp); 80 | Assert.Equal(labels, info.Labels); 81 | } 82 | 83 | [Fact] 84 | public void TestAddWithUncompressed() 85 | { 86 | IDatabase db = redisFixture.Redis.GetDatabase(); 87 | db.TimeSeriesCreate(key); 88 | TimeStamp now = DateTime.UtcNow; 89 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.1, uncompressed: true)); 90 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 91 | Assert.Equal(now, info.FirstTimeStamp); 92 | Assert.Equal(now, info.LastTimeStamp); 93 | } 94 | 95 | [Fact] 96 | public void TestAddWithChunkSize() 97 | { 98 | IDatabase db = redisFixture.Redis.GetDatabase(); 99 | TimeStamp now = DateTime.UtcNow; 100 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.1, chunkSizeBytes: 128)); 101 | TimeSeriesInformation info = db.TimeSeriesInfo(key); 102 | Assert.Equal(now, info.FirstTimeStamp); 103 | Assert.Equal(now, info.LastTimeStamp); 104 | Assert.Equal(128, info.ChunkSize); 105 | } 106 | 107 | [Fact] 108 | public void TestAddWithDuplicatePolicyBlock() 109 | { 110 | IDatabase db = redisFixture.Redis.GetDatabase(); 111 | TimeStamp now = DateTime.UtcNow; 112 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.1)); 113 | Assert.Throws(() => db.TimeSeriesAdd(key, now, 1.2)); 114 | } 115 | 116 | [Fact] 117 | public void TestAddWithDuplicatePolicyMin() 118 | { 119 | IDatabase db = redisFixture.Redis.GetDatabase(); 120 | TimeStamp now = DateTime.UtcNow; 121 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.1)); 122 | 123 | // Insert a bigger number and check that it did not change the value. 124 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.2, duplicatePolicy: TsDuplicatePolicy.MIN)); 125 | Assert.Equal(1.1, db.TimeSeriesRange(key, now, now)[0].Val); 126 | // Insert a smaller number and check that it changed. 127 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.0, duplicatePolicy: TsDuplicatePolicy.MIN)); 128 | Assert.Equal(1.0, db.TimeSeriesRange(key, now, now)[0].Val); 129 | } 130 | 131 | [Fact] 132 | public void TestAddWithDuplicatePolicyMax() 133 | { 134 | IDatabase db = redisFixture.Redis.GetDatabase(); 135 | TimeStamp now = DateTime.UtcNow; 136 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.1)); 137 | 138 | // Insert a smaller number and check that it did not change the value. 139 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.0, duplicatePolicy: TsDuplicatePolicy.MAX)); 140 | Assert.Equal(1.1, db.TimeSeriesRange(key, now, now)[0].Val); 141 | // Insert a bigger number and check that it changed. 142 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.2, duplicatePolicy: TsDuplicatePolicy.MAX)); 143 | Assert.Equal(1.2, db.TimeSeriesRange(key, now, now)[0].Val); 144 | } 145 | 146 | [Fact] 147 | public void TestAddWithDuplicatePolicySum() 148 | { 149 | IDatabase db = redisFixture.Redis.GetDatabase(); 150 | TimeStamp now = DateTime.UtcNow; 151 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.1)); 152 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.0, duplicatePolicy: TsDuplicatePolicy.SUM)); 153 | Assert.Equal(2.1, db.TimeSeriesRange(key, now, now)[0].Val); 154 | } 155 | 156 | [Fact] 157 | public void TestAddWithDuplicatePolicyFirst() 158 | { 159 | IDatabase db = redisFixture.Redis.GetDatabase(); 160 | TimeStamp now = DateTime.UtcNow; 161 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.1)); 162 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.0, duplicatePolicy: TsDuplicatePolicy.FIRST)); 163 | Assert.Equal(1.1, db.TimeSeriesRange(key, now, now)[0].Val); 164 | } 165 | 166 | [Fact] 167 | public void TestAddWithDuplicatePolicyLast() 168 | { 169 | IDatabase db = redisFixture.Redis.GetDatabase(); 170 | TimeStamp now = DateTime.UtcNow; 171 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.1)); 172 | Assert.Equal(now, db.TimeSeriesAdd(key, now, 1.0, duplicatePolicy: TsDuplicatePolicy.LAST)); 173 | Assert.Equal(1.0, db.TimeSeriesRange(key, now, now)[0].Val); 174 | } 175 | 176 | [Fact] 177 | public void TestOldAdd() 178 | { 179 | TimeStamp old_dt = DateTime.UtcNow; 180 | Thread.Sleep(1000); 181 | TimeStamp new_dt = DateTime.UtcNow; 182 | IDatabase db = redisFixture.Redis.GetDatabase(); 183 | db.TimeSeriesCreate(key); 184 | db.TimeSeriesAdd(key, new_dt, 1.1); 185 | // Adding old event 186 | Assert.Equal( old_dt, db.TimeSeriesAdd(key, old_dt, 1.1)); 187 | } 188 | 189 | [Fact] 190 | public void TestWrongParameters() 191 | { 192 | double value = 1.1; 193 | IDatabase db = redisFixture.Redis.GetDatabase(); 194 | var ex = Assert.Throws(() => db.TimeSeriesAdd(key, "+", value)); 195 | Assert.Equal("ERR TSDB: invalid timestamp", ex.Message); 196 | ex = Assert.Throws(() => db.TimeSeriesAdd(key, "-", value)); 197 | Assert.Equal("ERR TSDB: invalid timestamp", ex.Message); 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/MRangeExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using StackExchange.Redis; 4 | using NRedisTimeSeries.Commands.Enums; 5 | using NRedisTimeSeries.DataTypes; 6 | 7 | namespace NRedisTimeSeries.Example 8 | { 9 | /// 10 | /// Examples for NRedisTimeSeries API for MRANGE queries. 11 | /// 12 | internal class MRangeExample 13 | { 14 | /// 15 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis and a filter. 16 | /// NRedisTimeSeris MRange is expecting two TimeStamps objects as the range boundries. 17 | /// In this case, the strings are implicitly casted into TimeStamp objects. 18 | /// The TimeSeriesMRange command returns an IReadOnlyList<(string key, IReadOnlyList labels, IReadOnlyList values)>collection. 19 | /// 20 | public static void BasicMRangeExample() 21 | { 22 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 23 | IDatabase db = redis.GetDatabase(); 24 | var filter = new List { "MRANGEkey=MRANGEvalue" }; 25 | var results = db.TimeSeriesMRange("-", "+", filter); 26 | // Values extraction example. No lables in this case. 27 | foreach (var result in results) 28 | { 29 | Console.WriteLine(result.key); 30 | IReadOnlyList values = result.values; 31 | foreach(TimeSeriesTuple val in values){ 32 | Console.WriteLine(val); 33 | } 34 | } 35 | redis.Close(); 36 | } 37 | 38 | /// 39 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis, a filter and the COUNT parameter. 40 | /// NRedisTimeSeris MRange is expecting two TimeStamps objects as the range boundries. 41 | /// In this case, the strings are implicitly casted into TimeStamp objects. 42 | /// The TimeSeriesMRange command returns an IReadOnlyList (collection) of (string key, IReadOnlyList(TimeSeriesLabel) labels, IReadOnlyList(TimeSeriesTuple) values). 43 | /// 44 | public static void CountMRangeExample() 45 | { 46 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 47 | IDatabase db = redis.GetDatabase(); 48 | var filter = new List { "MRANGEkey=MRANGEvalue" }; 49 | var results = db.TimeSeriesMRange("-", "+", filter, count: 50); 50 | // Values extraction example. No lables in this case. 51 | foreach (var result in results) 52 | { 53 | Console.WriteLine(result.key); 54 | IReadOnlyList values = result.values; 55 | foreach(TimeSeriesTuple val in values){ 56 | Console.WriteLine(val); 57 | } 58 | } 59 | redis.Close(); 60 | } 61 | 62 | /// 63 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis, a filter and MIN aggregation. 64 | /// NRedisTimeSeris MRange is expecting two TimeStamps objects as the range boundries. 65 | /// In this case, the strings are implicitly casted into TimeStamp objects. 66 | /// The TimeSeriesMRange command returns an IReadOnlyList (collection) of (string key, IReadOnlyList(TimeSeriesLabel) labels, IReadOnlyList(TimeSeriesTuple) values). 67 | /// 68 | public static void MRangeAggregationExample() 69 | { 70 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 71 | IDatabase db = redis.GetDatabase(); 72 | var filter = new List { "MRANGEkey=MRANGEvalue" }; 73 | var results = db.TimeSeriesMRange("-", "+", filter, aggregation: TsAggregation.Min, timeBucket: 50); 74 | // Values extraction example. No lables in this case. 75 | foreach (var result in results) 76 | { 77 | Console.WriteLine(result.key); 78 | IReadOnlyList values = result.values; 79 | foreach(TimeSeriesTuple val in values){ 80 | Console.WriteLine(val); 81 | } 82 | } 83 | redis.Close(); 84 | } 85 | 86 | /// 87 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis, a filter and WITHLABELS flag. 88 | /// NRedisTimeSeris MRange is expecting two TimeStamps objects as the range boundries. 89 | /// In this case, the strings are implicitly casted into TimeStamp objects. 90 | /// The TimeSeriesMRange command returns an IReadOnlyList (collection) of (string key, IReadOnlyList(TimeSeriesLabel) labels, IReadOnlyList(TimeSeriesTuple) values). 91 | /// 92 | public static void MRangeWithLabelsExample() 93 | { 94 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 95 | IDatabase db = redis.GetDatabase(); 96 | var filter = new List { "MRANGEkey=MRANGEvalue" }; 97 | var results = db.TimeSeriesMRange("-", "+", filter, withLabels: true); 98 | // Values extraction example. 99 | foreach (var result in results) 100 | { 101 | Console.WriteLine(result.key); 102 | IReadOnlyList labels = result.labels; 103 | foreach(TimeSeriesLabel label in labels){ 104 | Console.WriteLine(label); 105 | } 106 | IReadOnlyList values = result.values; 107 | foreach(TimeSeriesTuple val in values){ 108 | Console.WriteLine(val); 109 | } 110 | } 111 | redis.Close(); 112 | } 113 | 114 | /// 115 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis, a filter and a Groupby concept. 116 | /// NRedisTimeSeris MRange is expecting two TimeStamps objects as the range boundries. 117 | /// In this case, the strings are implicitly casted into TimeStamp objects. 118 | /// The TimeSeriesMRange command returns an IReadOnlyList (collection) of (string key, IReadOnlyList(TimeSeriesLabel) labels, IReadOnlyList(TimeSeriesTuple) values). 119 | /// 120 | public static void MRangeWithGroupbyExample() 121 | { 122 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 123 | IDatabase db = redis.GetDatabase(); 124 | var filter = new List { "MRANGEkey=MRANGEvalue" }; 125 | var results = db.TimeSeriesMRange("-", "+", filter, withLabels: true, groupbyTuple: ("labelName", TsReduce.Max)); 126 | // Values extraction example. 127 | foreach (var result in results) 128 | { 129 | Console.WriteLine(result.key); 130 | IReadOnlyList labels = result.labels; 131 | foreach(TimeSeriesLabel label in labels){ 132 | Console.WriteLine(label); 133 | } 134 | IReadOnlyList values = result.values; 135 | foreach(TimeSeriesTuple val in values){ 136 | Console.WriteLine(val); 137 | } 138 | } 139 | redis.Close(); 140 | } 141 | 142 | /// 143 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis, and a FILTER_BY concept. 144 | /// NRedisTimeSeris MRange is expecting two TimeStamps objects as the range boundries. 145 | /// In this case, the strings are implicitly casted into TimeStamp objects. 146 | /// The TimeSeriesMRange command returns an IReadOnlyList (collection) of (string key, IReadOnlyList(TimeSeriesLabel) labels, IReadOnlyList(TimeSeriesTuple) values). 147 | /// 148 | public static void MRangeWithFilterbyExample() 149 | { 150 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 151 | IDatabase db = redis.GetDatabase(); 152 | var filter = new List { "MRANGEkey=MRANGEvalue" }; 153 | var results = db.TimeSeriesMRange("-", "+", filter, filterByTs: new List {0}, filterByValue: (0, 2)); 154 | // Values extraction example. No lables in this case. 155 | foreach (var result in results) 156 | { 157 | Console.WriteLine(result.key); 158 | IReadOnlyList values = result.values; 159 | foreach(TimeSeriesTuple val in values){ 160 | Console.WriteLine(val); 161 | } 162 | } 163 | redis.Close(); 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /NRedisTimeSeries/TimeSeriesClientResponseParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NRedisTimeSeries.Commands.Enums; 4 | using NRedisTimeSeries.DataTypes; 5 | using NRedisTimeSeries.Extensions; 6 | using StackExchange.Redis; 7 | 8 | namespace NRedisTimeSeries 9 | { 10 | public static partial class TimeSeriesClient 11 | { 12 | private static bool ParseBoolean(RedisResult result) 13 | { 14 | return (string)result == "OK"; 15 | } 16 | 17 | private static long ParseLong(RedisResult result) 18 | { 19 | if (result.Type == ResultType.None) return 0; 20 | return (long)result; 21 | } 22 | 23 | private static TimeStamp ParseTimeStamp(RedisResult result) 24 | { 25 | if (result.Type == ResultType.None) return null; 26 | return new TimeStamp((long)result); 27 | } 28 | 29 | private static IReadOnlyList ParseTimeStampArray(RedisResult result) 30 | { 31 | RedisResult[] redisResults = (RedisResult[])result; 32 | var list = new List(redisResults.Length); 33 | if (redisResults.Length == 0) return list; 34 | Array.ForEach(redisResults, timestamp => list.Add(ParseTimeStamp(timestamp))); 35 | return list; 36 | } 37 | 38 | private static TimeSeriesTuple ParseTimeSeriesTuple(RedisResult result) 39 | { 40 | RedisResult[] redisResults = (RedisResult[])result; 41 | if (redisResults.Length == 0) return null; 42 | return new TimeSeriesTuple(ParseTimeStamp(redisResults[0]), (double)redisResults[1]); 43 | } 44 | 45 | private static IReadOnlyList ParseTimeSeriesTupleArray(RedisResult result) 46 | { 47 | RedisResult[] redisResults = (RedisResult[])result; 48 | var list = new List(redisResults.Length); 49 | if (redisResults.Length == 0) return list; 50 | Array.ForEach(redisResults, tuple => list.Add(ParseTimeSeriesTuple(tuple))); 51 | return list; 52 | } 53 | 54 | private static IReadOnlyList ParseLabelArray(RedisResult result) 55 | { 56 | RedisResult[] redisResults = (RedisResult[])result; 57 | var list = new List(redisResults.Length); 58 | if (redisResults.Length == 0) return list; 59 | Array.ForEach(redisResults, labelResult => 60 | { 61 | RedisResult[] labelTuple = (RedisResult[])labelResult; 62 | list.Add(new TimeSeriesLabel((string)labelTuple[0], (string)labelTuple[1])); 63 | }); 64 | return list; 65 | } 66 | 67 | private static IReadOnlyList<(string key, IReadOnlyList labels, TimeSeriesTuple value)> ParseMGetesponse(RedisResult result) 68 | { 69 | RedisResult[] redisResults = (RedisResult[])result; 70 | var list = new List<(string key, IReadOnlyList labels, TimeSeriesTuple values)>(redisResults.Length); 71 | if (redisResults.Length == 0) return list; 72 | Array.ForEach(redisResults, MRangeValue => 73 | { 74 | RedisResult[] MRangeTuple = (RedisResult[])MRangeValue; 75 | string key = (string)MRangeTuple[0]; 76 | IReadOnlyList labels = ParseLabelArray(MRangeTuple[1]); 77 | TimeSeriesTuple value = ParseTimeSeriesTuple(MRangeTuple[2]); 78 | list.Add((key, labels, value)); 79 | }); 80 | return list; 81 | } 82 | 83 | private static IReadOnlyList<(string key, IReadOnlyList labels, IReadOnlyList values)> ParseMRangeResponse(RedisResult result) 84 | { 85 | RedisResult[] redisResults = (RedisResult[])result; 86 | var list = new List<(string key, IReadOnlyList labels, IReadOnlyList values)>(redisResults.Length); 87 | if (redisResults.Length == 0) return list; 88 | Array.ForEach(redisResults, MRangeValue => 89 | { 90 | RedisResult[] MRangeTuple = (RedisResult[])MRangeValue; 91 | string key = (string)MRangeTuple[0]; 92 | IReadOnlyList labels = ParseLabelArray(MRangeTuple[1]); 93 | IReadOnlyList values = ParseTimeSeriesTupleArray(MRangeTuple[2]); 94 | list.Add((key, labels, values)); 95 | }); 96 | return list; 97 | } 98 | 99 | private static TimeSeriesRule ParseRule(RedisResult result) 100 | { 101 | RedisResult[] redisResults = (RedisResult[])result; 102 | string destKey = (string)redisResults[0]; 103 | long bucketTime = (long)redisResults[1]; 104 | var aggregation = AggregationExtensions.AsAggregation((string)redisResults[2]); 105 | return new TimeSeriesRule(destKey, bucketTime, aggregation); 106 | } 107 | 108 | private static IReadOnlyList ParseRuleArray(RedisResult result) 109 | { 110 | RedisResult[] redisResults = (RedisResult[])result; 111 | var list = new List(); 112 | if (redisResults.Length == 0) return list; 113 | Array.ForEach(redisResults, rule => list.Add(ParseRule(rule))); 114 | return list; 115 | } 116 | 117 | private static TsDuplicatePolicy? ParsePolicy(RedisResult result) 118 | { 119 | var policyStatus = (string) result; 120 | if (String.IsNullOrEmpty(policyStatus) || policyStatus == "(nil)") { 121 | return null; 122 | } 123 | 124 | return DuplicatePolicyExtensions.AsPolicy(policyStatus.ToUpper()); 125 | } 126 | 127 | private static TimeSeriesInformation ParseInfo(RedisResult result) 128 | { 129 | long totalSamples = -1, memoryUsage = -1, retentionTime = -1, chunkSize=-1, chunkCount = -1; 130 | TimeStamp firstTimestamp = null, lastTimestamp = null; 131 | IReadOnlyList labels = null; 132 | IReadOnlyList rules = null; 133 | string sourceKey = null; 134 | TsDuplicatePolicy? duplicatePolicy = null; 135 | RedisResult[] redisResults = (RedisResult[])result; 136 | for(int i=0; i v1.4 176 | duplicatePolicy = ParsePolicy(redisResults[i]); 177 | break; 178 | } 179 | } 180 | 181 | return new TimeSeriesInformation(totalSamples, memoryUsage, firstTimestamp, 182 | lastTimestamp, retentionTime, chunkCount, chunkSize, labels, sourceKey, rules, duplicatePolicy); 183 | } 184 | 185 | private static IReadOnlyList ParseStringArray(RedisResult result) 186 | { 187 | RedisResult[] redisResults = (RedisResult[])result; 188 | var list = new List(); 189 | if (redisResults.Length == 0) return list; 190 | Array.ForEach(redisResults, str => list.Add((string)str)); 191 | return list; 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /NRedisTimeSeries.Example/MRangeExampleAsync.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Collections.Generic; 4 | using StackExchange.Redis; 5 | using NRedisTimeSeries.Commands.Enums; 6 | using NRedisTimeSeries.DataTypes; 7 | 8 | namespace NRedisTimeSeries.Example 9 | { 10 | /// 11 | /// Examples for NRedisTimeSeries async API for MRANGE queries. 12 | /// 13 | internal class MRangeAsyncExample 14 | { 15 | /// 16 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis and a filter. 17 | /// NRedisTimeSeris MRange is expecting two TimeStamps objects as the range boundries. 18 | /// In this case, the strings are implicitly casted into TimeStamp objects. 19 | /// The TimeSeriesMRange command returns an IReadOnlyList (collection) of (string key, IReadOnlyList(TimeSeriesLabel) labels, IReadOnlyList(TimeSeriesTuple) values). 20 | /// 21 | public static async Task BasicMRangeAsyncExample() 22 | { 23 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 24 | IDatabase db = redis.GetDatabase(); 25 | var filter = new List { "MRANGEkey=MRANGEvalue" }; 26 | var results = await db.TimeSeriesMRangeAsync("-", "+", filter); 27 | // Values extraction example. No lables in this case. 28 | foreach (var result in results) 29 | { 30 | Console.WriteLine(result.key); 31 | IReadOnlyList values = result.values; 32 | foreach(TimeSeriesTuple val in values){ 33 | Console.WriteLine(val); 34 | } 35 | } 36 | redis.Close(); 37 | } 38 | 39 | /// 40 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis, a filter and the COUNT parameter. 41 | /// NRedisTimeSeris MRange is expecting two TimeStamps objects as the range boundries. 42 | /// In this case, the strings are implicitly casted into TimeStamp objects. 43 | /// The TimeSeriesMRange command returns an IReadOnlyList (collection) of (string key, IReadOnlyList(TimeSeriesLabel) labels, IReadOnlyList(TimeSeriesTuple) values). 44 | /// 45 | public static async Task CountMRangeAsyncExample() 46 | { 47 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 48 | IDatabase db = redis.GetDatabase(); 49 | var filter = new List { "MRANGEkey=MRANGEvalue" }; 50 | var results = await db.TimeSeriesMRangeAsync("-", "+", filter, count: 50); 51 | // Values extraction example. No lables in this case. 52 | foreach (var result in results) 53 | { 54 | Console.WriteLine(result.key); 55 | IReadOnlyList values = result.values; 56 | foreach(TimeSeriesTuple val in values){ 57 | Console.WriteLine(val); 58 | } 59 | } 60 | redis.Close(); 61 | } 62 | 63 | /// 64 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis, a filter and MIN aggregation. 65 | /// NRedisTimeSeris MRange is expecting two TimeStamps objects as the range boundries. 66 | /// In this case, the strings are implicitly casted into TimeStamp objects. 67 | /// The TimeSeriesMRange command returns an IReadOnlyList (collection) of (string key, IReadOnlyList(TimeSeriesLabel) labels, IReadOnlyList(TimeSeriesTuple) values). 68 | /// 69 | public static async Task MRangeAggregationAsyncExample() 70 | { 71 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 72 | IDatabase db = redis.GetDatabase(); 73 | var filter = new List { "MRANGEkey=MRANGEvalue" }; 74 | var results = await db.TimeSeriesMRangeAsync("-", "+", filter, aggregation: TsAggregation.Min, timeBucket: 50); 75 | // Values extraction example. No lables in this case. 76 | foreach (var result in results) 77 | { 78 | Console.WriteLine(result.key); 79 | IReadOnlyList values = result.values; 80 | foreach(TimeSeriesTuple val in values){ 81 | Console.WriteLine(val); 82 | } 83 | } 84 | redis.Close(); 85 | } 86 | 87 | /// 88 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis, a filter and WITHLABELS flag. 89 | /// NRedisTimeSeris MRange is expecting two TimeStamps objects as the range boundries. 90 | /// In this case, the strings are implicitly casted into TimeStamp objects. 91 | /// The TimeSeriesMRange command returns an IReadOnlyList (collection) of (string key, IReadOnlyList(TimeSeriesLabel) labels, IReadOnlyList(TimeSeriesTuple) values). 92 | /// 93 | public static async Task MRangeWithLabelsAsyncExample() 94 | { 95 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 96 | IDatabase db = redis.GetDatabase(); 97 | var filter = new List { "MRANGEkey=MRANGEvalue" }; 98 | var results = await db.TimeSeriesMRangeAsync("-", "+", filter, withLabels: true); 99 | // Values extraction example. 100 | foreach (var result in results) 101 | { 102 | Console.WriteLine(result.key); 103 | IReadOnlyList labels = result.labels; 104 | foreach(TimeSeriesLabel label in labels){ 105 | Console.WriteLine(label); 106 | } 107 | IReadOnlyList values = result.values; 108 | foreach(TimeSeriesTuple val in values){ 109 | Console.WriteLine(val); 110 | } 111 | } 112 | redis.Close(); 113 | } 114 | 115 | /// 116 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis, a filter and a Groupby concept. 117 | /// NRedisTimeSeris MRange is expecting two TimeStamps objects as the range boundries. 118 | /// In this case, the strings are implicitly casted into TimeStamp objects. 119 | /// The TimeSeriesMRange command returns an IReadOnlyList (collection) of (string key, IReadOnlyList(TimeSeriesLabel) labels, IReadOnlyList(TimeSeriesTuple) values). 120 | /// 121 | public static async Task MRangeWithGroupbyAsyncExample() 122 | { 123 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 124 | IDatabase db = redis.GetDatabase(); 125 | var filter = new List { "MRANGEkey=MRANGEvalue" }; 126 | var results = await db.TimeSeriesMRangeAsync("-", "+", filter, withLabels: true, groupbyTuple: ("labelName", TsReduce.Max)); 127 | // Values extraction example. 128 | foreach (var result in results) 129 | { 130 | Console.WriteLine(result.key); 131 | IReadOnlyList labels = result.labels; 132 | foreach(TimeSeriesLabel label in labels){ 133 | Console.WriteLine(label); 134 | } 135 | IReadOnlyList values = result.values; 136 | foreach(TimeSeriesTuple val in values){ 137 | Console.WriteLine(val); 138 | } 139 | } 140 | redis.Close(); 141 | } 142 | 143 | /// 144 | /// Example for basic usage of RedisTimeSeries RANGE command with "-" and "+" as range boundreis, and a FILTER_BY concept. 145 | /// NRedisTimeSeris MRange is expecting two TimeStamps objects as the range boundries. 146 | /// In this case, the strings are implicitly casted into TimeStamp objects. 147 | /// The TimeSeriesMRange command returns an IReadOnlyList (collection) of (string key, IReadOnlyList(TimeSeriesLabel) labels, IReadOnlyList(TimeSeriesTuple) values). 148 | /// 149 | public static async Task MRangeWithFilterbyExample() 150 | { 151 | ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 152 | IDatabase db = redis.GetDatabase(); 153 | var filter = new List { "MRANGEkey=MRANGEvalue" }; 154 | var results = await db.TimeSeriesMRangeAsync("-", "+", filter, filterByTs: new List {0}, filterByValue: (0, 2)); 155 | // Values extraction example. No lables in this case. 156 | foreach (var result in results) 157 | { 158 | Console.WriteLine(result.key); 159 | IReadOnlyList values = result.values; 160 | foreach(TimeSeriesTuple val in values){ 161 | Console.WriteLine(val); 162 | } 163 | } 164 | redis.Close(); 165 | } 166 | } 167 | } 168 | --------------------------------------------------------------------------------