Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CMS Commands #5

Merged
merged 3 commits into from
Aug 8, 2022
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: 1 addition & 1 deletion src/NRedisStack.Core/Bloom/BloomCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public bool Exists(RedisKey key, RedisValue item)
/// <returns>An array of booleans. Each element is either true or false depending on whether the
/// corresponding input element was newly added to the filter or may have previously existed.</returns>
/// <remarks><seealso href="https://redis.io/commands/bf.insert"/></remarks>
public bool[] Insert(RedisKey key, RedisValue[] items, int? capacity = null,
public bool[] Insert(RedisKey key, RedisValue[] items, int? capacity = null, //TODO: create enother function that get one item, because right now if the user want to insert one item he needs to insert this as RedisValue[]
double? error = null, int? expansion = null,
bool nocreate = false, bool nonscaling = false)
{
Expand Down
135 changes: 135 additions & 0 deletions src/NRedisStack.Core/CountMinSketch/CmsCommands.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using NRedisStack.Core.CountMinSketch.DataTypes;
using NRedisStack.Core.Literals;
using StackExchange.Redis;
namespace NRedisStack.Core
{

public class CmsCommands
{
IDatabase _db;
public CmsCommands(IDatabase db)
{
_db = db;
}

/// <summary>
/// Increases the count of item by increment.
/// </summary>
/// <param name="key">The name of the sketch.</param>
/// <param name="item">The item which counter is to be increased.</param>
/// <param name="increment">Amount by which the item counter is to be increased.</param>
/// <returns>Count of each item after increment.</returns>
/// <remarks><seealso href="https://redis.io/commands/cms.incrby"/></remarks>
public long IncrBy(RedisKey key, RedisValue item, long increment)
{
return ResponseParser.ToLong(_db.Execute(CMS.INCRBY, key, item, increment));
}

/// <summary>
/// Increases the count of item by increment.
/// </summary>
/// <param name="key">The name of the sketch.</param>
/// <param name="itemIncrements">Tuple of The items which counter is to be increased
/// and the Amount by which the item counter is to be increased.</param>
/// <returns>Count of each item after increment.</returns>
/// <remarks><seealso href="https://redis.io/commands/cms.incrby"/></remarks>
public long[]? IncrBy(RedisKey key, Tuple<RedisValue, long>[] itemIncrements)
{
if (itemIncrements.Length < 1)
throw new ArgumentException(nameof(itemIncrements));

List<object> args = new List<object> { key };
foreach (var pair in itemIncrements)
{
args.Add(pair.Item1);
args.Add(pair.Item2);
}
return ResponseParser.ToLongArray(_db.Execute(CMS.INCRBY, args));
}

/// <summary>
/// Return information about a sketch.
/// </summary>
/// <param name="key">Name of the key to return information about.</param>
/// <returns>Information of the sketch.</returns>
/// <remarks><seealso href="https://redis.io/commands/cms.info"/></remarks>
public CmsInformation? Info(RedisKey key)
{
var info = _db.Execute(CMS.INFO, key);
return ResponseParser.ToCmsInfo(info);
}

//TODO: functions that returns OK cannot return false, they return OK or ERROR. fix this (the Redis functions that can return also false - will return 1 for true ans 0 for false)
/// <summary>
/// Initializes a Count-Min Sketch to dimensions specified by user.
/// </summary>
/// <param name="key">TThe name of the sketch.</param>
/// <param name="width">Number of counters in each array. Reduces the error size.</param>
/// <param name="depth">Number of counter-arrays. Reduces the probability for an error
/// of a certain size (percentage of total count).</param>
/// <returns><see langword="true"/> if if executed correctly, Error otherwise.</returns>
/// <remarks><seealso href="https://redis.io/commands/cms.initbydim"/></remarks>
public bool InitByDim(RedisKey key, long width, long depth)
{
return ResponseParser.ParseOKtoBoolean(_db.Execute(CMS.INITBYDIM, key, width, depth));
}

/// <summary>
/// Initializes a Count-Min Sketch to accommodate requested tolerances.
/// </summary>
/// <param name="key">The name of the sketch.</param>
/// <param name="error">Estimate size of error.</param>
/// <param name="probability">The desired probability for inflated count.</param>
/// <returns><see langword="true"/> if if executed correctly, Error otherwise.</returns>
/// <remarks><seealso href="https://redis.io/commands/cms.initbyprob"/></remarks>
public bool InitByProb(RedisKey key, double error, double probability)
{
return ResponseParser.ParseOKtoBoolean(_db.Execute(CMS.INITBYPROB, key, error, probability));
}

/// <summary>
/// Merges several sketches into one sketch.
/// </summary>
/// <param name="destination">The name of destination sketch. Must be initialized</param>
/// <param name="numKeys">Number of sketches to be merged.</param>
/// <param name="source">Names of source sketches to be merged.</param>
/// <param name="weight">Multiple of each sketch. Default = 1.</param>
/// <returns><see langword="true"/> if if executed correctly, Error otherwise.</returns>
/// <remarks><seealso href="https://redis.io/commands/cms.merge"/></remarks>
public bool Merge(RedisValue destination, long numKeys, RedisValue[] source, long[]? weight = null)
{
if (source.Length < 1)
throw new ArgumentNullException(nameof(source));

List<object> args = new List<object> { destination, numKeys };

foreach (var s in source) args.Add(s);

if (weight != null && weight.Length >= 1)
{
args.Add(CmsArgs.WEIGHTS);
foreach (var w in weight) args.Add(w);
}

return ResponseParser.ParseOKtoBoolean(_db.Execute(CMS.MERGE, args));
}

/// <summary>
/// Returns the count for one or more items in a sketch.
/// </summary>
/// <param name="key">The name of the sketch</param>
/// <param name="items">One or more items for which to return the count.</param>
/// <returns>Array with a min-count of each of the items in the sketch</returns>
/// <remarks><seealso href="https://redis.io/commands/cms.merge"/></remarks>
public long[]? Query(RedisKey key, RedisValue[] items) //TODO: Create second version of this function using params for items input
{
if (items.Length < 1)
throw new ArgumentNullException(nameof(items));

List<object> args = new List<object> { key };
foreach (var item in items) args.Add(item);

return ResponseParser.ToLongArray(_db.Execute(CMS.QUERY, args));
}
}
}
21 changes: 21 additions & 0 deletions src/NRedisStack.Core/CountMinSketch/DataTypes/CmsInformation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace NRedisStack.Core.CountMinSketch.DataTypes
{
/// <summary>
/// This class represents the response for CMS.INFO command.
/// This object has Read-only properties and cannot be generated outside a CMS.INFO response.
/// </summary>
public class CmsInformation
{
public long Width { get; private set; }
public long Depth { get; private set; }
public long Count { get; private set; }


internal CmsInformation(long width, long depth, long count)
{
Width = width;
Depth = depth;
Count = count;
}
}
}
13 changes: 13 additions & 0 deletions src/NRedisStack.Core/CountMinSketch/Literals/CommandArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace NRedisStack.Core.Literals
{
internal class CmsArgs
{
public static string WEIGHTS => "WEIGHTS";
// public static string CAPACITY => "CAPACITY";
// public static string EXPANSION => "EXPANSION";
// public static string NOCREATE => "NOCREATE";
// public static string ITEMS => "ITEMS";
// public static string BUCKETSIZE => "BUCKETSIZE";
// public static string MAXITERATIONS => "MAXITERATIONS";
}
}
12 changes: 12 additions & 0 deletions src/NRedisStack.Core/CountMinSketch/Literals/Commands.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace NRedisStack.Core.Literals
{
internal class CMS
{
public static string INITBYDIM => "CMS.INITBYDIM";
public static string INITBYPROB => "CMS.INITBYPROB";
public static string INCRBY => "CMS.INCRBY";
public static string QUERY => "CMS.QUERY";
public static string MERGE => "CMS.MERGE";
public static string INFO => "CMS.INFO";
}
}
14 changes: 14 additions & 0 deletions src/NRedisStack.Core/ModulPrefixes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ public static class ModulPrefixes
static bool cuckooCreated = false;
static CuckooCommands cuckooCommands;

static bool cmsCreated = false;
static CmsCommands cmsCommands;

static bool searchCreated = false;
static SearchCommands searchCommands;

Expand Down Expand Up @@ -41,6 +44,17 @@ static public CuckooCommands CF(this IDatabase db)
return cuckooCommands;
}

static public CmsCommands CMS(this IDatabase db)
{
if (!cmsCreated)
{
cmsCommands = new CmsCommands(db);
cmsCreated = true;
}

return cmsCommands;
}

static public SearchCommands FT(this IDatabase db)
{
if (!searchCreated)
Expand Down
39 changes: 39 additions & 0 deletions src/NRedisStack.Core/ResponseParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using StackExchange.Redis;
using NRedisStack.Core.Bloom.DataTypes;
using NRedisStack.Core.CuckooFilter.DataTypes;
using NRedisStack.Core.CountMinSketch.DataTypes;

namespace NRedisStack.Core
{
Expand Down Expand Up @@ -41,6 +42,12 @@ public static long ToLong(RedisResult result)
return (long)result;
}

public static long[]? ToLongArray(RedisResult result)
{
if (result.Type == ResultType.None) return null;
return (long[])result;
}

public static TimeStamp ToTimeStamp(RedisResult result)
{
if (result.Type == ResultType.None) return null;
Expand Down Expand Up @@ -206,6 +213,7 @@ public static IReadOnlyList<TimeSeriesRule> ToRuleArray(RedisResult result)

return new BloomInformation(capacity, size, numberOfFilters, numberOfItemsInserted, expansionRate);
}

public static CuckooInformation? ToCuckooInfo(RedisResult result) //TODO: Think about a different implementation, because if the output of BF.INFO changes or even just the names of the labels then the parsing will not work
{
long size, numberOfBuckets, numberOfFilter, numberOfItemsInserted,
Expand Down Expand Up @@ -256,6 +264,37 @@ public static IReadOnlyList<TimeSeriesRule> ToRuleArray(RedisResult result)
numberOfItemsDeleted, bucketSize, expansionRate, maxIteration);
}

public static CmsInformation? ToCmsInfo(RedisResult result) //TODO: Think about a different implementation, because if the output of CMS.INFO changes or even just the names of the labels then the parsing will not work
{
long width, depth, count;

width = depth = count = -1;

RedisResult[]? redisResults = (RedisResult[]?)result;

if (redisResults == null) return null;

for (int i = 0; i < redisResults.Length; ++i)
{
string? label = redisResults[i++].ToString();

switch (label)
{
case "width":
width = (long)redisResults[i];
break;
case "depth":
depth = (long)redisResults[i];
break;
case "count":
count = (long)redisResults[i];
break;
}
}

return new CmsInformation(width, depth, count);
}

public static TimeSeriesInformation ToTimeSeriesInfo(RedisResult result)
{
long totalSamples = -1, memoryUsage = -1, retentionTime = -1, chunkSize = -1, chunkCount = -1;
Expand Down
Loading