Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/NRedisStack/PublicAPI/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,8 @@ NRedisStack.Literals.Enums.TsAggregation.StdP = 8 -> NRedisStack.Literals.Enums.
NRedisStack.Literals.Enums.TsAggregation.StdS = 9 -> NRedisStack.Literals.Enums.TsAggregation
NRedisStack.Literals.Enums.TsAggregation.Sum = 1 -> NRedisStack.Literals.Enums.TsAggregation
NRedisStack.Literals.Enums.TsAggregation.Twa = 12 -> NRedisStack.Literals.Enums.TsAggregation
NRedisStack.Literals.Enums.TsAggregation.CountNan = 13 -> NRedisStack.Literals.Enums.TsAggregation
NRedisStack.Literals.Enums.TsAggregation.CountAll = 14 -> NRedisStack.Literals.Enums.TsAggregation
NRedisStack.Literals.Enums.TsAggregation.VarP = 10 -> NRedisStack.Literals.Enums.TsAggregation
NRedisStack.Literals.Enums.TsAggregation.VarS = 11 -> NRedisStack.Literals.Enums.TsAggregation
NRedisStack.Literals.Enums.TsBucketTimestamps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ internal static class AggregationExtensions
TsAggregation.VarP => "var.p",
TsAggregation.VarS => "var.s",
TsAggregation.Twa => "twa",
TsAggregation.CountNan => "countnan",
TsAggregation.CountAll => "countall",
_ => throw new ArgumentOutOfRangeException(nameof(aggregation), "Invalid aggregation type"),
};

Expand Down Expand Up @@ -50,6 +52,8 @@ internal static class AggregationExtensions
"VAR.P" => TsAggregation.VarP,
"VAR.S" => TsAggregation.VarS,
"TWA" => TsAggregation.Twa,
"COUNTNAN" => TsAggregation.CountNan,
"COUNTALL" => TsAggregation.CountAll,
_ => throw new ArgumentOutOfRangeException(nameof(aggregation), $"Invalid aggregation type '{aggregation}'"),
};
}
12 changes: 12 additions & 0 deletions src/NRedisStack/TimeSeries/Literals/Enums/Aggregation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,16 @@ public enum TsAggregation
/// Time-weighted average of all values
/// </summary>
Twa,

/// <summary>
/// Count of NaN values in the aggregation bucket
/// </summary>
/// <remarks>Available since Redis 8.6.0</remarks>
CountNan,

/// <summary>
/// Count of all values (including NaN) in the aggregation bucket
/// </summary>
/// <remarks>Available since Redis 8.6.0</remarks>
CountAll,
}
3 changes: 2 additions & 1 deletion src/NRedisStack/TimeSeries/TimeSeriesAux.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using NRedisStack.Literals.Enums;
using NRedisStack.DataTypes;
using NRedisStack.Extensions;
using StackExchange.Redis;

namespace NRedisStack;

Expand Down Expand Up @@ -195,7 +196,7 @@ public static List<object> BuildRangeArgs(string key,
TsBucketTimestamps? bt,
bool empty)
{
var args = new List<object>() { key, fromTimeStamp.Value, toTimeStamp.Value };
var args = new List<object>() { (RedisKey)key, fromTimeStamp.Value, toTimeStamp.Value };
args.AddLatest(latest);
args.AddFilterByTs(filterByTs);
args.AddFilterByValue(filterByValue);
Expand Down
44 changes: 44 additions & 0 deletions tests/NRedisStack.Tests/TimeSeries/TestAPI/TestRange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,4 +277,48 @@ public void TestEmpty()
Assert.Equal(range[i].Val, expected[i].Val);
}
}

[SkipIfRedisTheory(Comparison.LessThan, "8.5.0")]
[MemberData(nameof(EndpointsFixture.Env.AllEnvironments), MemberType = typeof(EndpointsFixture.Env))]
public void TestRangeCountNanAggregation(string endpointId)
{
IDatabase db = GetCleanDatabase(endpointId);
var ts = db.TS();
var key = CreateKeyName();

// Create a time series and add data including NaN values
ts.Create(key);
ts.Add(key, 10, 1.0);
ts.Add(key, 20, double.NaN);
ts.Add(key, 30, 3.0);
ts.Add(key, 40, double.NaN);
ts.Add(key, 50, 5.0);

// Test CountNan aggregation - should count NaN values
var range = ts.Range(key, 0, 100, aggregation: TsAggregation.CountNan, timeBucket: 100);
Assert.Single(range);
Assert.Equal(2, range[0].Val); // 2 NaN values
}

[SkipIfRedisTheory(Comparison.LessThan, "8.5.0")]
[MemberData(nameof(EndpointsFixture.Env.AllEnvironments), MemberType = typeof(EndpointsFixture.Env))]
public void TestRangeCountAllAggregation(string endpointId)
{
IDatabase db = GetCleanDatabase(endpointId);
var ts = db.TS();
var key = CreateKeyName();

// Create a time series and add data including NaN values
ts.Create(key);
ts.Add(key, 10, 1.0);
ts.Add(key, 20, double.NaN);
ts.Add(key, 30, 3.0);
ts.Add(key, 40, double.NaN);
ts.Add(key, 50, 5.0);

// Test CountAll aggregation - should count all values including NaN
var range = ts.Range(key, 0, 100, aggregation: TsAggregation.CountAll, timeBucket: 100);
Assert.Single(range);
Assert.Equal(5, range[0].Val); // 5 total values (3 regular + 2 NaN)
}
}
44 changes: 44 additions & 0 deletions tests/NRedisStack.Tests/TimeSeries/TestAPI/TestRangeAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -283,4 +283,48 @@ public async Task TestEmptyAsync()
Assert.Equal(range[i].Val, expected[i].Val);
}
}

[SkipIfRedisTheory(Comparison.LessThan, "8.5.0")]
[MemberData(nameof(EndpointsFixture.Env.AllEnvironments), MemberType = typeof(EndpointsFixture.Env))]
public async Task TestRangeCountNanAggregationAsync(string endpointId)
{
IDatabase db = GetCleanDatabase(endpointId);
var ts = db.TS();
var key = CreateKeyName();

// Create a time series and add data including NaN values
await ts.CreateAsync(key);
await ts.AddAsync(key, 10, 1.0);
await ts.AddAsync(key, 20, double.NaN);
await ts.AddAsync(key, 30, 3.0);
await ts.AddAsync(key, 40, double.NaN);
await ts.AddAsync(key, 50, 5.0);

// Test CountNan aggregation - should count NaN values
var range = await ts.RangeAsync(key, 0, 100, aggregation: TsAggregation.CountNan, timeBucket: 100);
Assert.Single(range);
Assert.Equal(2, range[0].Val); // 2 NaN values
}

[SkipIfRedisTheory(Comparison.LessThan, "8.5.0")]
[MemberData(nameof(EndpointsFixture.Env.AllEnvironments), MemberType = typeof(EndpointsFixture.Env))]
public async Task TestRangeCountAllAggregationAsync(string endpointId)
{
IDatabase db = GetCleanDatabase(endpointId);
var ts = db.TS();
var key = CreateKeyName();

// Create a time series and add data including NaN values
await ts.CreateAsync(key);
await ts.AddAsync(key, 10, 1.0);
await ts.AddAsync(key, 20, double.NaN);
await ts.AddAsync(key, 30, 3.0);
await ts.AddAsync(key, 40, double.NaN);
await ts.AddAsync(key, 50, 5.0);

// Test CountAll aggregation - should count all values including NaN
var range = await ts.RangeAsync(key, 0, 100, aggregation: TsAggregation.CountAll, timeBucket: 100);
Assert.Single(range);
Assert.Equal(5, range[0].Val); // 5 total values (3 regular + 2 NaN)
}
}
6 changes: 5 additions & 1 deletion tests/NRedisStack.Tests/TimeSeries/TestAPI/TestRulesAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ public async Task TestRulesAdditionDeletion(string endpointId)
var db = GetCleanDatabase(endpointId);
var ts = db.TS();
await ts.CreateAsync(key);
var aggregations = (TsAggregation[])Enum.GetValues(typeof(TsAggregation));
var allAggregations = (TsAggregation[])Enum.GetValues(typeof(TsAggregation));
// Filter out CountNan and CountAll on Redis versions < 8.6.0 as they are not supported
var aggregations = EndpointsFixture.RedisVersion >= new Version("8.5.0")
? allAggregations
: allAggregations.Where(a => a != TsAggregation.CountNan && a != TsAggregation.CountAll).ToArray();

foreach (var aggregation in aggregations)
{
Expand Down