add project
This commit is contained in:
302
Commands/AdvancedStringCommands.cs
Normal file
302
Commands/AdvancedStringCommands.cs
Normal file
@ -0,0 +1,302 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedisManager.Utils;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains command line options and implementations for advanced Redis string operations.
|
||||
/// Provides functionality for APPEND, INCR, DECR, INCRBY, DECRBY, INCRBYFLOAT, GETRANGE, SETRANGE, STRLEN, MGET, and MSET commands.
|
||||
/// </summary>
|
||||
[Verb("append", HelpText = "Append a value to a key.")]
|
||||
public class AppendOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "value", Required = true, HelpText = "Value to append.")]
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
[Verb("incr", HelpText = "Increment the integer value of a key by one.")]
|
||||
public class IncrOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
|
||||
public string Key { get; set; }
|
||||
}
|
||||
|
||||
[Verb("decr", HelpText = "Decrement the integer value of a key by one.")]
|
||||
public class DecrOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
|
||||
public string Key { get; set; }
|
||||
}
|
||||
|
||||
[Verb("incrby", HelpText = "Increment the integer value of a key by a given amount.")]
|
||||
public class IncrByOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "amount", Required = true, HelpText = "Amount to increment by.")]
|
||||
public long Amount { get; set; }
|
||||
}
|
||||
|
||||
[Verb("decrby", HelpText = "Decrement the integer value of a key by a given amount.")]
|
||||
public class DecrByOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "amount", Required = true, HelpText = "Amount to decrement by.")]
|
||||
public long Amount { get; set; }
|
||||
}
|
||||
|
||||
[Verb("incrbyfloat", HelpText = "Increment the float value of a key by a given amount.")]
|
||||
public class IncrByFloatOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "amount", Required = true, HelpText = "Amount to increment by.")]
|
||||
public double Amount { get; set; }
|
||||
}
|
||||
|
||||
[Verb("getrange", HelpText = "Get a substring of the string stored at a key.")]
|
||||
public class GetRangeOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "start", Required = true, HelpText = "Start offset.")]
|
||||
public long Start { get; set; }
|
||||
[Value(2, MetaName = "end", Required = true, HelpText = "End offset.")]
|
||||
public long End { get; set; }
|
||||
}
|
||||
|
||||
[Verb("setrange", HelpText = "Overwrite part of a string at key starting at the specified offset.")]
|
||||
public class SetRangeOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "offset", Required = true, HelpText = "Offset.")]
|
||||
public long Offset { get; set; }
|
||||
[Value(2, MetaName = "value", Required = true, HelpText = "Value to set.")]
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
[Verb("strlen", HelpText = "Get the length of the value stored in a key.")]
|
||||
public class StrLenOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key.")]
|
||||
public string Key { get; set; }
|
||||
}
|
||||
|
||||
[Verb("mget", HelpText = "Get the values of all the given keys.")]
|
||||
public class MGetOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "keys", Min = 1, Required = true, HelpText = "Keys.")]
|
||||
public IEnumerable<string> Keys { get; set; }
|
||||
}
|
||||
|
||||
[Verb("mset", HelpText = "Set multiple keys to multiple values.")]
|
||||
public class MSetOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Option("pairs", Required = true, HelpText = "Comma-separated key=value pairs.")]
|
||||
public string Pairs { get; set; }
|
||||
}
|
||||
|
||||
public static class AdvancedStringCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the APPEND command to append a value to a key.
|
||||
/// </summary>
|
||||
/// <param name="opts">The AppendOptions containing instance, key, and value.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunAppend(AppendOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var len = db.StringAppend(opts.Key, opts.Value);
|
||||
Console.WriteLine(Output.Green(len.ToString()));
|
||||
return 0;
|
||||
}
|
||||
/// <summary>
|
||||
/// Executes the INCR command to increment the integer value of a key by one.
|
||||
/// </summary>
|
||||
/// <param name="opts">The IncrOptions containing instance and key.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunIncr(IncrOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var val = db.StringIncrement(opts.Key);
|
||||
Console.WriteLine(Output.Green(val.ToString()));
|
||||
return 0;
|
||||
}
|
||||
/// <summary>
|
||||
/// Executes the DECR command to decrement the integer value of a key by one.
|
||||
/// </summary>
|
||||
/// <param name="opts">The DecrOptions containing instance and key.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunDecr(DecrOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var val = db.StringDecrement(opts.Key);
|
||||
Console.WriteLine(Output.Green(val.ToString()));
|
||||
return 0;
|
||||
}
|
||||
/// <summary>
|
||||
/// Executes the INCRBY command to increment the integer value of a key by a given amount.
|
||||
/// </summary>
|
||||
/// <param name="opts">The IncrByOptions containing instance, key, and amount.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunIncrBy(IncrByOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var val = db.StringIncrement(opts.Key, opts.Amount);
|
||||
Console.WriteLine(Output.Green(val.ToString()));
|
||||
return 0;
|
||||
}
|
||||
/// <summary>
|
||||
/// Executes the DECRBY command to decrement the integer value of a key by a given amount.
|
||||
/// </summary>
|
||||
/// <param name="opts">The DecrByOptions containing instance, key, and amount.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunDecrBy(DecrByOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var val = db.StringDecrement(opts.Key, opts.Amount);
|
||||
Console.WriteLine(Output.Green(val.ToString()));
|
||||
return 0;
|
||||
}
|
||||
/// <summary>
|
||||
/// Executes the INCRBYFLOAT command to increment the float value of a key by a given amount.
|
||||
/// </summary>
|
||||
/// <param name="opts">The IncrByFloatOptions containing instance, key, and amount.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunIncrByFloat(IncrByFloatOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var val = db.StringIncrement(opts.Key, opts.Amount);
|
||||
Console.WriteLine(Output.Green(val.ToString()));
|
||||
return 0;
|
||||
}
|
||||
/// <summary>
|
||||
/// Executes the GETRANGE command to get a substring of the string stored at a key.
|
||||
/// </summary>
|
||||
/// <param name="opts">The GetRangeOptions containing instance, key, start, and end.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunGetRange(GetRangeOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var val = db.StringGetRange(opts.Key, opts.Start, opts.End);
|
||||
Console.WriteLine(Output.Green(val.ToString()));
|
||||
return 0;
|
||||
}
|
||||
/// <summary>
|
||||
/// Executes the SETRANGE command to overwrite part of a string at key starting at the specified offset.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SetRangeOptions containing instance, key, offset, and value.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSetRange(SetRangeOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var len = db.StringSetRange(opts.Key, opts.Offset, opts.Value);
|
||||
Console.WriteLine(Output.Green(len.ToString()));
|
||||
return 0;
|
||||
}
|
||||
/// <summary>
|
||||
/// Executes the STRLEN command to get the length of the value stored in a key.
|
||||
/// </summary>
|
||||
/// <param name="opts">The StrLenOptions containing instance and key.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunStrLen(StrLenOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var len = db.StringLength(opts.Key);
|
||||
Console.WriteLine(Output.Green(len.ToString()));
|
||||
return 0;
|
||||
}
|
||||
/// <summary>
|
||||
/// Executes the MGET command to get the values of all the given keys.
|
||||
/// </summary>
|
||||
/// <param name="opts">The MGetOptions containing instance and keys.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunMGet(MGetOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
|
||||
var vals = db.StringGet(keys);
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
{
|
||||
Console.WriteLine(Output.Green($"{keys[i]}: {vals[i]}"));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/// <summary>
|
||||
/// Executes the MSET command to set multiple keys to multiple values.
|
||||
/// </summary>
|
||||
/// <param name="opts">The MSetOptions containing instance and pairs.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunMSet(MSetOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var pairs = opts.Pairs.Split(',').Select(p => p.Split('=', 2)).Where(p => p.Length == 2).Select(p => new KeyValuePair<RedisKey, RedisValue>(p[0].Trim(), p[1].Trim())).ToArray();
|
||||
db.StringSet(pairs);
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
207
Commands/BitCommands.cs
Normal file
207
Commands/BitCommands.cs
Normal file
@ -0,0 +1,207 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedisManager.Utils;
|
||||
|
||||
using RedisManager.Commands;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
[Verb("bitcount", HelpText = "Count set bits in a string.")]
|
||||
public class BitCountOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
|
||||
public string Key { get; set; }
|
||||
[Option("start", Default = 0L, HelpText = "Start byte.")]
|
||||
public long Start { get; set; }
|
||||
[Option("end", Default = -1L, HelpText = "End byte.")]
|
||||
public long End { get; set; }
|
||||
}
|
||||
|
||||
[Verb("bitfield", HelpText = "Perform arbitrary bitfield integer operations on strings.")]
|
||||
public class BitFieldOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "operations", Min = 1, Required = true, HelpText = "Bitfield operations.")]
|
||||
public IEnumerable<string> Operations { get; set; }
|
||||
}
|
||||
|
||||
[Verb("bitop", HelpText = "Perform bitwise operations between strings.")]
|
||||
public class BitOpOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "operation", Required = true, HelpText = "Operation (AND, OR, XOR, NOT).")]
|
||||
public string Operation { get; set; }
|
||||
[Value(1, MetaName = "dest-key", Required = true, HelpText = "Destination key.")]
|
||||
public string DestKey { get; set; }
|
||||
[Value(2, MetaName = "source-keys", Min = 1, Required = true, HelpText = "Source keys.")]
|
||||
public IEnumerable<string> SourceKeys { get; set; }
|
||||
}
|
||||
|
||||
[Verb("bitpos", HelpText = "Find first bit set or clear in a string.")]
|
||||
public class BitPosOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "bit", Required = true, HelpText = "Bit value (0 or 1).")]
|
||||
public int Bit { get; set; }
|
||||
[Option("start", Default = 0L, HelpText = "Start byte.")]
|
||||
public long Start { get; set; }
|
||||
[Option("end", Default = -1L, HelpText = "End byte.")]
|
||||
public long End { get; set; }
|
||||
}
|
||||
|
||||
[Verb("getbit", HelpText = "Returns the bit value at offset in the string value stored at key.")]
|
||||
public class GetBitOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "offset", Required = true, HelpText = "Bit offset.")]
|
||||
public long Offset { get; set; }
|
||||
}
|
||||
|
||||
[Verb("setbit", HelpText = "Sets or clears the bit at offset in the string value stored at key.")]
|
||||
public class SetBitOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "offset", Required = true, HelpText = "Bit offset.")]
|
||||
public long Offset { get; set; }
|
||||
[Value(2, MetaName = "bit", Required = true, HelpText = "Bit value (0 or 1).")]
|
||||
public int Bit { get; set; }
|
||||
}
|
||||
|
||||
public static class BitCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the BITCOUNT command to count set bits in a string.
|
||||
/// </summary>
|
||||
/// <param name="opts">The BitCountOptions containing instance, key, start, and end.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunBitCount(BitCountOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var args = new List<object> { opts.Key };
|
||||
if (opts.Start != 0 || opts.End != -1) { args.Add(opts.Start); args.Add(opts.End); }
|
||||
var result = db.Execute("BITCOUNT", args.ToArray());
|
||||
Console.WriteLine(Output.Green($"Bit count: {result}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the BITFIELD command to perform arbitrary bitfield integer operations on strings.
|
||||
/// </summary>
|
||||
/// <param name="opts">The BitFieldOptions containing instance, key, and operations.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunBitField(BitFieldOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var args = new List<object> { opts.Key };
|
||||
args.AddRange(opts.Operations);
|
||||
var result = db.Execute("BITFIELD", args.ToArray());
|
||||
RedisResult[] results;
|
||||
if (result is Array)
|
||||
results = (RedisResult[])result;
|
||||
else
|
||||
results = new RedisResult[] { result };
|
||||
|
||||
Console.WriteLine(Output.Green("Bitfield results:"));
|
||||
var operationsList = opts.Operations.ToList();
|
||||
for (int i = 0; results != null && i < results.Length; i++)
|
||||
{
|
||||
var res = results[i];
|
||||
if (res != null)
|
||||
{
|
||||
Console.WriteLine(Output.Cyan($"{operationsList[i]}: {res}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(Output.Red($"{operationsList[i]}: null"));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the BITOP command to perform bitwise operations between strings.
|
||||
/// </summary>
|
||||
/// <param name="opts">The BitOpOptions containing instance, operation, destination key, and source keys.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunBitOp(BitOpOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var args = new List<object> { opts.Operation, opts.DestKey };
|
||||
args.AddRange(opts.SourceKeys);
|
||||
var result = db.Execute("BITOP", args.ToArray());
|
||||
Console.WriteLine(Output.Green($"BITOP result: {result}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the BITPOS command to find the first bit set or clear in a string.
|
||||
/// </summary>
|
||||
/// <param name="opts">The BitPosOptions containing instance, key, bit, start, and end.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunBitPos(BitPosOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var args = new List<object> { opts.Key, opts.Bit };
|
||||
if (opts.Start != 0 || opts.End != -1) { args.Add(opts.Start); args.Add(opts.End); }
|
||||
var result = db.Execute("BITPOS", args.ToArray());
|
||||
Console.WriteLine(Output.Green($"First {opts.Bit} bit at position: {result}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the GETBIT command to return the bit value at offset in the string value stored at key.
|
||||
/// </summary>
|
||||
/// <param name="opts">The GetBitOptions containing instance, key, and offset.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunGetBit(GetBitOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var result = db.Execute("GETBIT", new object[] { opts.Key, opts.Offset });
|
||||
Console.WriteLine(Output.Green($"Bit at offset {opts.Offset}: {result}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SETBIT command to set or clear the bit at offset in the string value stored at key.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SetBitOptions containing instance, key, offset, and bit value.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSetBit(SetBitOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var result = db.Execute("SETBIT", new object[] { opts.Key, opts.Offset, opts.Bit });
|
||||
Console.WriteLine(Output.Green($"Set bit at offset {opts.Offset} to {opts.Bit}: {result}"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
160
Commands/ConnectionCommands.cs
Normal file
160
Commands/ConnectionCommands.cs
Normal file
@ -0,0 +1,160 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedisManager.Utils;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains command line options and implementations for Redis connection management operations.
|
||||
/// Provides functionality for AUTH, QUIT, CLIENT LIST, CLIENT KILL, CLIENT GETNAME, and CLIENT SETNAME commands.
|
||||
/// </summary>
|
||||
[Verb("auth", HelpText = "Authenticate to the server.")]
|
||||
public class AuthOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "password", Required = true, HelpText = "Password.")]
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
[Verb("quit", HelpText = "Close the connection.")]
|
||||
public class QuitOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
}
|
||||
|
||||
[Verb("clientlist", HelpText = "Get the list of client connections.")]
|
||||
public class ClientListOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
}
|
||||
|
||||
[Verb("clientkill", HelpText = "Kill a client connection.")]
|
||||
public class ClientKillOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "client_id", Required = true, HelpText = "Client ID.")]
|
||||
public string ClientId { get; set; }
|
||||
}
|
||||
|
||||
[Verb("clientgetname", HelpText = "Get the current connection name.")]
|
||||
public class ClientGetNameOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
}
|
||||
|
||||
[Verb("clientsetname", HelpText = "Set the current connection name.")]
|
||||
public class ClientSetNameOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "name", Required = true, HelpText = "Connection name.")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
public static class ConnectionCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the AUTH command to authenticate to the server.
|
||||
/// </summary>
|
||||
/// <param name="opts">The AuthOptions containing instance and password.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunAuth(AuthOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
db.Execute("AUTH", opts.Password);
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the QUIT command to close the connection.
|
||||
/// </summary>
|
||||
/// <param name="opts">The QuitOptions containing instance.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunQuit(QuitOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
db.Execute("QUIT");
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the CLIENT LIST command to get the list of client connections.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ClientListOptions containing instance.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunClientList(ClientListOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var result = db.Execute("CLIENT", "LIST");
|
||||
Console.WriteLine(Output.Green(result.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the CLIENT KILL command to kill a client connection.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ClientKillOptions containing instance and client ID.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunClientKill(ClientKillOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
db.Execute("CLIENT", "KILL", opts.ClientId);
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the CLIENT GETNAME command to get the current connection name.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ClientGetNameOptions containing instance.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunClientGetName(ClientGetNameOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var result = db.Execute("CLIENT", "GETNAME");
|
||||
Console.WriteLine(Output.Green(result.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the CLIENT SETNAME command to set the current connection name.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ClientSetNameOptions containing instance and name.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunClientSetName(ClientSetNameOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
db.Execute("CLIENT", "SETNAME", opts.Name);
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
121
Commands/DatabaseCommands.cs
Normal file
121
Commands/DatabaseCommands.cs
Normal file
@ -0,0 +1,121 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedisManager.Utils;
|
||||
|
||||
using RedisManager.Commands;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
[Verb("select", HelpText = "Change the selected database.")]
|
||||
public class SelectOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "database", Required = true, HelpText = "Database number.")]
|
||||
public int Database { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("flushdb", HelpText = "Remove all keys from the current database.")]
|
||||
public class FlushDbOptions : InstanceOptions
|
||||
{
|
||||
[Option("yes", Required = false, HelpText = "Skip confirmation prompt.")]
|
||||
public bool Yes { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("flushall", HelpText = "Remove all keys from all databases.")]
|
||||
public class FlushAllOptions : InstanceOptions
|
||||
{
|
||||
[Option("yes", Required = false, HelpText = "Skip confirmation prompt.")]
|
||||
public bool Yes { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("dbsize", HelpText = "Return the number of keys in the current database.")]
|
||||
public class DbSizeOptions : InstanceOptions
|
||||
{
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
public static class DatabaseCommands
|
||||
{
|
||||
public static int RunSelect(SelectOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase(opts.Database);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Database", "Status" }, new List<string[]> { new[] { opts.Database.ToString(), "Selected" } });
|
||||
else
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int RunFlushDb(FlushDbOptions opts, Config config)
|
||||
{
|
||||
if (!opts.Yes)
|
||||
{
|
||||
Console.WriteLine(Output.Yellow("Are you sure you want to flush the current database? (y/N)"));
|
||||
var response = Console.ReadLine()?.ToLower();
|
||||
if (response != "y" && response != "yes")
|
||||
{
|
||||
Console.WriteLine(Output.Red("Operation cancelled."));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
db.Execute("FLUSHDB");
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Operation", "Status" }, new List<string[]> { new[] { "FLUSHDB", "OK" } });
|
||||
else
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int RunFlushAll(FlushAllOptions opts, Config config)
|
||||
{
|
||||
if (!opts.Yes)
|
||||
{
|
||||
Console.WriteLine(Output.Yellow("Are you sure you want to flush ALL databases? (y/N)"));
|
||||
var response = Console.ReadLine()?.ToLower();
|
||||
if (response != "y" && response != "yes")
|
||||
{
|
||||
Console.WriteLine(Output.Red("Operation cancelled."));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
db.Execute("FLUSHALL");
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Operation", "Status" }, new List<string[]> { new[] { "FLUSHALL", "OK" } });
|
||||
else
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int RunDbSize(DbSizeOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var size = db.Execute("DBSIZE");
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Database", "Size" }, new List<string[]> { new[] { "Current", size.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(size.ToString()));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
242
Commands/GeoCommands.cs
Normal file
242
Commands/GeoCommands.cs
Normal file
@ -0,0 +1,242 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedisManager.Utils;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains command line options and implementations for Redis geospatial operations.
|
||||
/// Provides functionality for GEOADD, GEORADIUS, GEODIST, GEOHASH, and GEOPOS commands.
|
||||
/// </summary>
|
||||
[Verb("geoadd", HelpText = "Add one or more geospatial items to a key.")]
|
||||
public class GeoAddOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "members", Min = 3, Required = true, HelpText = "Members with longitude,latitude,name format.")]
|
||||
public IEnumerable<string> Members { get; set; }
|
||||
}
|
||||
|
||||
[Verb("georadius", HelpText = "Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a point.")]
|
||||
public class GeoRadiusOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "longitude", Required = true, HelpText = "Longitude.")]
|
||||
public double Longitude { get; set; }
|
||||
[Value(2, MetaName = "latitude", Required = true, HelpText = "Latitude.")]
|
||||
public double Latitude { get; set; }
|
||||
[Value(3, MetaName = "radius", Required = true, HelpText = "Radius.")]
|
||||
public double Radius { get; set; }
|
||||
[Option("unit", Default = "m", HelpText = "Unit (m, km, mi, ft).")]
|
||||
public string Unit { get; set; }
|
||||
[Option("withcoord", HelpText = "Include coordinates in output.")]
|
||||
public bool WithCoord { get; set; }
|
||||
[Option("withdist", HelpText = "Include distance in output.")]
|
||||
public bool WithDist { get; set; }
|
||||
[Option("withhash", HelpText = "Include hash in output.")]
|
||||
public bool WithHash { get; set; }
|
||||
[Option("count", HelpText = "Limit results count.")]
|
||||
public int Count { get; set; }
|
||||
[Option("asc", HelpText = "Sort ascending by distance.")]
|
||||
public bool Asc { get; set; }
|
||||
[Option("desc", HelpText = "Sort descending by distance.")]
|
||||
public bool Desc { get; set; }
|
||||
}
|
||||
|
||||
[Verb("geodist", HelpText = "Return the distance between two members of a geospatial index.")]
|
||||
public class GeoDistOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "member1", Required = true, HelpText = "First member.")]
|
||||
public string Member1 { get; set; }
|
||||
[Value(2, MetaName = "member2", Required = true, HelpText = "Second member.")]
|
||||
public string Member2 { get; set; }
|
||||
[Option("unit", Default = "m", HelpText = "Unit (m, km, mi, ft).")]
|
||||
public string Unit { get; set; }
|
||||
}
|
||||
|
||||
[Verb("geohash", HelpText = "Returns members of a geospatial index as standard geohash strings.")]
|
||||
public class GeoHashOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "members", Min = 1, Required = true, HelpText = "Members.")]
|
||||
public IEnumerable<string> Members { get; set; }
|
||||
}
|
||||
|
||||
[Verb("geopos", HelpText = "Returns longitude and latitude of members of a geospatial index.")]
|
||||
public class GeoPosOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "members", Min = 1, Required = true, HelpText = "Members.")]
|
||||
public IEnumerable<string> Members { get; set; }
|
||||
}
|
||||
|
||||
public static class GeoCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the GEOADD command to add one or more geospatial items to a key.
|
||||
/// </summary>
|
||||
/// <param name="opts">The GeoAddOptions containing instance, key, and members.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunGeoAdd(GeoAddOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var args = new List<object> { opts.Key };
|
||||
var membersList = opts.Members.ToList();
|
||||
for (int i = 0; i + 2 < membersList.Count; i += 3)
|
||||
{
|
||||
args.Add(membersList[i]); // longitude
|
||||
args.Add(membersList[i + 1]); // latitude
|
||||
args.Add(membersList[i + 2]); // name
|
||||
}
|
||||
var result = db.Execute("GEOADD", args.ToArray());
|
||||
Console.WriteLine(Output.Green($"Added: {result}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the GEORADIUS command to query a geospatial index for members within a radius.
|
||||
/// </summary>
|
||||
/// <param name="opts">The GeoRadiusOptions containing instance, key, longitude, latitude, radius, and options.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunGeoRadius(GeoRadiusOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var args = new List<object> { opts.Key, opts.Longitude, opts.Latitude, opts.Radius, opts.Unit };
|
||||
if (opts.WithCoord) args.Add("WITHCOORD");
|
||||
if (opts.WithDist) args.Add("WITHDIST");
|
||||
if (opts.WithHash) args.Add("WITHHASH");
|
||||
if (opts.Count > 0) { args.Add("COUNT"); args.Add(opts.Count); }
|
||||
if (opts.Asc) args.Add("ASC");
|
||||
if (opts.Desc) args.Add("DESC");
|
||||
var result = db.Execute("GEORADIUS", args.ToArray());
|
||||
Console.WriteLine(Output.Green($"Raw result: {result}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the GEODIST command to return the distance between two members of a geospatial index.
|
||||
/// </summary>
|
||||
/// <param name="opts">The GeoDistOptions containing instance, key, member1, member2, and unit.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunGeoDist(GeoDistOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var result = db.Execute("GEODIST", new object[] { opts.Key, opts.Member1, opts.Member2, opts.Unit });
|
||||
Console.WriteLine(Output.Green($"Distance: {result} {opts.Unit}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the GEOHASH command to return geohash strings for members of a geospatial index.
|
||||
/// </summary>
|
||||
/// <param name="opts">The GeoHashOptions containing instance, key, and members.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunGeoHash(GeoHashOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var args = new List<object> { opts.Key };
|
||||
args.AddRange(opts.Members);
|
||||
var result = db.Execute("GEOHASH", args.ToArray());
|
||||
RedisResult[] hashes;
|
||||
if (result is Array)
|
||||
hashes = (RedisResult[])result;
|
||||
else
|
||||
hashes = new RedisResult[] { result };
|
||||
var membersList = opts.Members.ToList();
|
||||
for (int i = 0; i < membersList.Count; i++)
|
||||
{
|
||||
var hash = hashes != null && i < hashes.Length ? hashes[i]?.ToString() : null;
|
||||
if (!string.IsNullOrEmpty(hash))
|
||||
{
|
||||
Console.WriteLine(Output.Green($"{membersList[i]}: {hash}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(Output.Red($"{membersList[i]}: not found"));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the GEOPOS command to return longitude and latitude of members of a geospatial index.
|
||||
/// </summary>
|
||||
/// <param name="opts">The GeoPosOptions containing instance, key, and members.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunGeoPos(GeoPosOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var args = new List<object> { opts.Key };
|
||||
args.AddRange(opts.Members);
|
||||
var result = db.Execute("GEOPOS", args.ToArray());
|
||||
RedisResult[] positions;
|
||||
if (result is Array)
|
||||
positions = (RedisResult[])result;
|
||||
else
|
||||
positions = new RedisResult[] { result };
|
||||
var membersList = opts.Members.ToList();
|
||||
for (int i = 0; i < membersList.Count; i++)
|
||||
{
|
||||
var pos = positions != null && i < positions.Length ? positions[i]?.ToString() : null;
|
||||
if (!string.IsNullOrEmpty(pos))
|
||||
{
|
||||
Console.WriteLine(Output.Green($"{membersList[i]}: {pos}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(Output.Red($"{membersList[i]}: not found"));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static GeoUnit GetGeoUnit(string unit)
|
||||
{
|
||||
return unit.ToLower() switch
|
||||
{
|
||||
"km" => GeoUnit.Kilometers,
|
||||
"mi" => GeoUnit.Miles,
|
||||
"ft" => GeoUnit.Feet,
|
||||
_ => GeoUnit.Meters
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
583
Commands/HashCommands.cs
Normal file
583
Commands/HashCommands.cs
Normal file
@ -0,0 +1,583 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedisManager.Utils;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains command line options and implementations for Redis hash operations.
|
||||
/// Provides functionality for HGET, HSET, HDEL, HGETALL, HKEYS, HVALS, HLEN, HEXISTS, HINCRBY, HINCRBYFLOAT, HMSET, HMGET, HSETNX, HSTRLEN, and HSCAN commands.
|
||||
/// </summary>
|
||||
[Verb("hget", HelpText = "Get value of a field in a hash.")]
|
||||
public class HGetOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
|
||||
public string Hash { get; set; }
|
||||
[Value(1, MetaName = "field", Required = true, HelpText = "Field name.")]
|
||||
public string Field { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("hset", HelpText = "Set value of a field in a hash.")]
|
||||
public class HSetOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
|
||||
public string Hash { get; set; }
|
||||
[Value(1, MetaName = "field", Required = true, HelpText = "Field name.")]
|
||||
public string Field { get; set; }
|
||||
[Value(2, MetaName = "value", Required = true, HelpText = "Value.")]
|
||||
public string Value { get; set; }
|
||||
[Option("ttl", Required = false, HelpText = "Time to live in seconds.")]
|
||||
public int? TTL { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("hdel", HelpText = "Delete a field in a hash.")]
|
||||
public class HDelOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
|
||||
public string Hash { get; set; }
|
||||
[Value(1, MetaName = "field", Required = true, HelpText = "Field name.")]
|
||||
public string Field { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("hgetall", HelpText = "Get all fields and values in a hash.")]
|
||||
public class HGetAllOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
|
||||
public string Hash { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("hkeys", HelpText = "List all fields in a hash.")]
|
||||
public class HKeysOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
|
||||
public string Hash { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("hvals", HelpText = "List all values in a hash.")]
|
||||
public class HValsOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
|
||||
public string Hash { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("hlen", HelpText = "Get the number of fields in a hash.")]
|
||||
public class HLenOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
|
||||
public string Hash { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("hexists", HelpText = "Check if a field exists in a hash.")]
|
||||
public class HExistsOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
|
||||
public string Hash { get; set; }
|
||||
[Value(1, MetaName = "field", Required = true, HelpText = "Field name.")]
|
||||
public string Field { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("hincrby", HelpText = "Increment field value in hash by integer.")]
|
||||
public class HIncrByOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
|
||||
public string Hash { get; set; }
|
||||
[Value(1, MetaName = "field", Required = true, HelpText = "Field name.")]
|
||||
public string Field { get; set; }
|
||||
[Value(2, MetaName = "increment", Required = true, HelpText = "Increment value.")]
|
||||
public long Increment { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("hincrbyfloat", HelpText = "Increment field value in hash by float.")]
|
||||
public class HIncrByFloatOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
|
||||
public string Hash { get; set; }
|
||||
[Value(1, MetaName = "field", Required = true, HelpText = "Field name.")]
|
||||
public string Field { get; set; }
|
||||
[Value(2, MetaName = "increment", Required = true, HelpText = "Increment value.")]
|
||||
public double Increment { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("hmset", HelpText = "Set multiple fields in a hash.")]
|
||||
public class HMSetOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
|
||||
public string Hash { get; set; }
|
||||
[Value(1, MetaName = "fields", Min = 1, Required = true, HelpText = "Field-value pairs (field1 value1 field2 value2...).")]
|
||||
public IEnumerable<string> Fields { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("hmget", HelpText = "Get multiple field values from a hash.")]
|
||||
public class HMGetOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
|
||||
public string Hash { get; set; }
|
||||
[Value(1, MetaName = "fields", Min = 1, Required = true, HelpText = "Field names to get.")]
|
||||
public IEnumerable<string> Fields { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("hsetnx", HelpText = "Set field value in hash only if field does not exist.")]
|
||||
public class HSetNxOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
|
||||
public string Hash { get; set; }
|
||||
[Value(1, MetaName = "field", Required = true, HelpText = "Field name.")]
|
||||
public string Field { get; set; }
|
||||
[Value(2, MetaName = "value", Required = true, HelpText = "Value.")]
|
||||
public string Value { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("hstrlen", HelpText = "Get the string length of a hash field value.")]
|
||||
public class HStrLenOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
|
||||
public string Hash { get; set; }
|
||||
[Value(1, MetaName = "field", Required = true, HelpText = "Field name.")]
|
||||
public string Field { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("hscan", HelpText = "Incrementally iterate hash fields.")]
|
||||
public class HScanOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "hash", Required = true, HelpText = "Hash key.")]
|
||||
public string Hash { get; set; }
|
||||
[Value(1, MetaName = "cursor", Required = true, HelpText = "Cursor position.")]
|
||||
public long Cursor { get; set; }
|
||||
[Option("match", Required = false, HelpText = "Pattern to match.")]
|
||||
public string Match { get; set; }
|
||||
[Option("count", Required = false, HelpText = "Count hint.")]
|
||||
public long? Count { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
public static class HashCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the HGET command to get the value of a field in a hash.
|
||||
/// </summary>
|
||||
/// <param name="opts">The HGetOptions containing instance, hash, and field.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunHGet(HGetOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var value = db.HashGet(opts.Hash, opts.Field);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Hash", "Field", "Value" }, new List<string[]> { new[] { opts.Hash, opts.Field, value.IsNull ? "[null]" : value.ToString() } });
|
||||
else if (value.IsNull)
|
||||
Console.WriteLine(Output.Yellow($"[null]"));
|
||||
else
|
||||
Console.WriteLine(Output.Green(value.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the HSET command to set the value of a field in a hash.
|
||||
/// </summary>
|
||||
/// <param name="opts">The HSetOptions containing instance, hash, field, and value.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunHSet(HSetOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var ok = db.HashSet(opts.Hash, opts.Field, opts.Value);
|
||||
if (opts.TTL.HasValue)
|
||||
db.KeyExpire(opts.Hash, TimeSpan.FromSeconds(opts.TTL.Value));
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Hash", "Field", "Value", "Result" }, new List<string[]> { new[] { opts.Hash, opts.Field, opts.Value, ok ? "OK" : "Failed" } });
|
||||
else if (ok)
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
else
|
||||
Console.WriteLine(Output.Red("Failed to set field."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the HDEL command to delete a field from a hash.
|
||||
/// </summary>
|
||||
/// <param name="opts">The HDelOptions containing instance, hash, and field.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunHDel(HDelOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var ok = db.HashDelete(opts.Hash, opts.Field);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Hash", "Field", "Result" }, new List<string[]> { new[] { opts.Hash, opts.Field, ok ? "Deleted" : "Not found" } });
|
||||
else if (ok)
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
else
|
||||
Console.WriteLine(Output.Yellow("Field not found."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the HGETALL command to get all fields and values in a hash.
|
||||
/// </summary>
|
||||
/// <param name="opts">The HGetAllOptions containing instance and hash.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunHGetAll(HGetAllOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var entries = db.HashGetAll(opts.Hash);
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = entries.Select(e => new[] { e.Name.ToString(), e.Value.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Field", "Value" }, rows);
|
||||
}
|
||||
else
|
||||
foreach (var entry in entries)
|
||||
Console.WriteLine(Output.Green($"{entry.Name}: {entry.Value}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the HKEYS command to list all fields in a hash.
|
||||
/// </summary>
|
||||
/// <param name="opts">The HKeysOptions containing instance and hash.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunHKeys(HKeysOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var keys = db.HashKeys(opts.Hash);
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = keys.Select(k => new[] { k.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Field" }, rows);
|
||||
}
|
||||
else
|
||||
foreach (var key in keys)
|
||||
Console.WriteLine(Output.Green(key.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the HVALS command to list all values in a hash.
|
||||
/// </summary>
|
||||
/// <param name="opts">The HValsOptions containing instance and hash.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunHVals(HValsOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var values = db.HashValues(opts.Hash);
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = values.Select(v => new[] { v.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Value" }, rows);
|
||||
}
|
||||
else
|
||||
foreach (var value in values)
|
||||
Console.WriteLine(Output.Green(value.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the HLEN command to get the number of fields in a hash.
|
||||
/// </summary>
|
||||
/// <param name="opts">The HLenOptions containing instance and hash.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunHLen(HLenOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var len = db.HashLength(opts.Hash);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Hash", "Length" }, new List<string[]> { new[] { opts.Hash, len.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(len.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the HEXISTS command to check if a field exists in a hash.
|
||||
/// </summary>
|
||||
/// <param name="opts">The HExistsOptions containing instance, hash, and field.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunHExists(HExistsOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var exists = db.HashExists(opts.Hash, opts.Field);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Hash", "Field", "Exists" }, new List<string[]> { new[] { opts.Hash, opts.Field, exists ? "Yes" : "No" } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(exists ? "1" : "0"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the HINCRBY command to increment a field value in a hash by an integer.
|
||||
/// </summary>
|
||||
/// <param name="opts">The HIncrByOptions containing instance, hash, field, and increment.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunHIncrBy(HIncrByOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var result = db.HashIncrement(opts.Hash, opts.Field, opts.Increment);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Hash", "Field", "Increment", "Result" }, new List<string[]> { new[] { opts.Hash, opts.Field, opts.Increment.ToString(), result.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(result.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the HINCRBYFLOAT command to increment a field value in a hash by a float.
|
||||
/// </summary>
|
||||
/// <param name="opts">The HIncrByFloatOptions containing instance, hash, field, and increment.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunHIncrByFloat(HIncrByFloatOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var result = db.HashIncrement(opts.Hash, opts.Field, opts.Increment);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Hash", "Field", "Increment", "Result" }, new List<string[]> { new[] { opts.Hash, opts.Field, opts.Increment.ToString(), result.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(result.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the HMSET command to set multiple fields in a hash.
|
||||
/// </summary>
|
||||
/// <param name="opts">The HMSetOptions containing instance, hash, and field-value pairs.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunHMSet(HMSetOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var fieldsList = opts.Fields.ToList();
|
||||
if (fieldsList.Count % 2 != 0)
|
||||
{
|
||||
Console.WriteLine(Output.Red("Error: Fields must be provided as field-value pairs."));
|
||||
return 1;
|
||||
}
|
||||
|
||||
var entries = new HashEntry[fieldsList.Count / 2];
|
||||
for (int i = 0; i < fieldsList.Count; i += 2)
|
||||
{
|
||||
entries[i / 2] = new HashEntry(fieldsList[i], fieldsList[i + 1]);
|
||||
}
|
||||
|
||||
db.HashSet(opts.Hash, entries);
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = new List<string[]>();
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
rows.Add(new[] { opts.Hash, entries[i].Name.ToString(), entries[i].Value.ToString() });
|
||||
}
|
||||
RedisUtils.PrintTable(new[] { "Hash", "Field", "Value" }, rows);
|
||||
}
|
||||
else
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the HMGET command to get multiple field values from a hash.
|
||||
/// </summary>
|
||||
/// <param name="opts">The HMGetOptions containing instance, hash, and fields.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunHMGet(HMGetOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var fields = opts.Fields.Select(f => (RedisValue)f).ToArray();
|
||||
var values = db.HashGet(opts.Hash, fields);
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = new List<string[]>();
|
||||
var fieldsList = opts.Fields.ToList();
|
||||
for (int i = 0; i < fieldsList.Count; i++)
|
||||
{
|
||||
var value = values[i].IsNull ? "[null]" : values[i].ToString();
|
||||
rows.Add(new[] { opts.Hash, fieldsList[i], value });
|
||||
}
|
||||
RedisUtils.PrintTable(new[] { "Hash", "Field", "Value" }, rows);
|
||||
}
|
||||
else
|
||||
{
|
||||
var fieldsList = opts.Fields.ToList();
|
||||
for (int i = 0; i < fieldsList.Count; i++)
|
||||
{
|
||||
var value = values[i].IsNull ? "[null]" : values[i].ToString();
|
||||
Console.WriteLine(Output.Green($"{fieldsList[i]}: {value}"));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the HSETNX command to set a field value in a hash only if the field does not exist.
|
||||
/// </summary>
|
||||
/// <param name="opts">The HSetNxOptions containing instance, hash, field, and value.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunHSetNx(HSetNxOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var result = db.HashSet(opts.Hash, opts.Field, opts.Value, When.NotExists);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Hash", "Field", "Value", "Result" }, new List<string[]> { new[] { opts.Hash, opts.Field, opts.Value, result ? "Set" : "Not set (field exists)" } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(result ? "1" : "0"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the HSTRLEN command to get the string length of a hash field value.
|
||||
/// </summary>
|
||||
/// <param name="opts">The HStrLenOptions containing instance, hash, and field.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunHStrLen(HStrLenOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var length = db.HashStringLength(opts.Hash, opts.Field);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Hash", "Field", "Length" }, new List<string[]> { new[] { opts.Hash, opts.Field, length.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(length.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the HSCAN command to incrementally iterate hash fields.
|
||||
/// </summary>
|
||||
/// <param name="opts">The HScanOptions containing instance, hash, and scan options.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunHScan(HScanOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var pattern = opts.Match;
|
||||
var pageSize = opts.Count.HasValue ? (int)opts.Count.Value : 10;
|
||||
var scanResult = db.HashScan(opts.Hash, pattern, pageSize);
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = scanResult.Select(e => new[] { e.Name.ToString(), e.Value.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Field", "Value" }, rows);
|
||||
Console.WriteLine(Output.Yellow($"Scanned {rows.Count} entries"));
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var entry in scanResult)
|
||||
{
|
||||
Console.WriteLine(Output.Green($"{entry.Name}: {entry.Value}"));
|
||||
count++;
|
||||
}
|
||||
Console.WriteLine(Output.Yellow($"Scanned {count} entries"));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
117
Commands/HyperLogLogCommands.cs
Normal file
117
Commands/HyperLogLogCommands.cs
Normal file
@ -0,0 +1,117 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedisManager.Utils;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
[Verb("pfadd", HelpText = "Add elements to a HyperLogLog.")]
|
||||
public class PFAddOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "HyperLogLog key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "elements", Min = 1, Required = true, HelpText = "Elements to add.")]
|
||||
public IEnumerable<string> Elements { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("pfcount", HelpText = "Get the approximated cardinality of HyperLogLog(s).")]
|
||||
public class PFCountOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "keys", Min = 1, Required = true, HelpText = "HyperLogLog key(s).")]
|
||||
public IEnumerable<string> Keys { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("pfmerge", HelpText = "Merge multiple HyperLogLogs into a single one.")]
|
||||
public class PFMergeOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "destkey", Required = true, HelpText = "Destination key.")]
|
||||
public string DestKey { get; set; }
|
||||
[Value(1, MetaName = "sourcekeys", Min = 1, Required = true, HelpText = "Source HyperLogLog keys.")]
|
||||
public IEnumerable<string> SourceKeys { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
public static class HyperLogLogCommands
|
||||
{
|
||||
public static int RunPFAdd(PFAddOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var elements = opts.Elements.ToArray();
|
||||
|
||||
// Use PFADD command directly
|
||||
var batch = db.CreateBatch();
|
||||
var tasks = elements.Select(e => batch.ExecuteAsync("PFADD", opts.Key, e)).ToArray();
|
||||
batch.Execute();
|
||||
|
||||
var count = tasks.Count(t => (bool)t.Result);
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = elements.Select(e => new[] { e, "Added" }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Element", "Result" }, rows);
|
||||
RedisUtils.PrintTable(new[] { "Key", "Added Count" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
|
||||
}
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Added {count} new element(s)"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int RunPFCount(PFCountOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var keys = opts.Keys.ToArray();
|
||||
|
||||
// Use PFCOUNT command directly
|
||||
var count = db.Execute("PFCOUNT", keys);
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
if (keys.Length == 1)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Cardinality" }, new List<string[]> { new[] { keys[0], count.ToString() } });
|
||||
else
|
||||
RedisUtils.PrintTable(new[] { "Keys", "Combined Cardinality" }, new List<string[]> { new[] { string.Join(", ", keys), count.ToString() } });
|
||||
}
|
||||
else
|
||||
Console.WriteLine(Output.Green(count.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int RunPFMerge(PFMergeOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var sourceKeys = opts.SourceKeys.ToArray();
|
||||
|
||||
// Use PFMERGE command directly
|
||||
db.Execute("PFMERGE", opts.DestKey, sourceKeys);
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = sourceKeys.Select(k => new[] { k, "Merged" }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Source Key", "Status" }, rows);
|
||||
RedisUtils.PrintTable(new[] { "Destination Key", "Status" }, new List<string[]> { new[] { opts.DestKey, "OK" } });
|
||||
}
|
||||
else
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
126
Commands/InstanceCommands.cs
Normal file
126
Commands/InstanceCommands.cs
Normal file
@ -0,0 +1,126 @@
|
||||
using CommandLine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using RedisManager.Utils;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
[Verb("list-instances", HelpText = "List all configured Redis instances.")]
|
||||
public class ListInstancesOptions { }
|
||||
|
||||
[Verb("add-instance", HelpText = "Add a new Redis instance.")]
|
||||
public class AddInstanceOptions
|
||||
{
|
||||
[Option('n', "name", Required = true, HelpText = "Instance name.")]
|
||||
public string Name { get; set; }
|
||||
[Option('h', "host", Required = true, HelpText = "Redis host.")]
|
||||
public string Host { get; set; }
|
||||
[Option('p', "port", Required = false, HelpText = "Redis port.")]
|
||||
public int Port { get; set; } = 6379;
|
||||
[Option('w', "password", Required = false, HelpText = "Redis password.")]
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
[Verb("update-instance", HelpText = "Update an existing Redis instance.")]
|
||||
public class UpdateInstanceOptions
|
||||
{
|
||||
[Option('n', "name", Required = true, HelpText = "Instance name.")]
|
||||
public string Name { get; set; }
|
||||
[Option('h', "host", Required = false, HelpText = "Redis host.")]
|
||||
public string Host { get; set; }
|
||||
[Option('p', "port", Required = false, HelpText = "Redis port.")]
|
||||
public int Port { get; set; } = 6379;
|
||||
[Option('w', "password", Required = false, HelpText = "Redis password.")]
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
[Verb("delete-instance", HelpText = "Delete a Redis instance.")]
|
||||
public class DeleteInstanceOptions
|
||||
{
|
||||
[Option('n', "name", Required = true, HelpText = "Instance name.")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
public static class InstanceCommands
|
||||
{
|
||||
public static int RunListInstances(Config config)
|
||||
{
|
||||
if (config.Instances.Count == 0)
|
||||
{
|
||||
Console.WriteLine(Output.Yellow("No instances configured."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
var rows = config.Instances.Select(i => new[] { i.Name, i.Host, i.Port.ToString(), i.Password ?? "" }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Name", "Host", "Port", "Password" }, rows);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int RunAddInstance(AddInstanceOptions opts, Config config)
|
||||
{
|
||||
if (config.Instances.Any(i => i.Name == opts.Name))
|
||||
{
|
||||
Console.WriteLine(Output.Red($"Instance '{opts.Name}' already exists."));
|
||||
return 1;
|
||||
}
|
||||
|
||||
var instance = new InstanceConfig
|
||||
{
|
||||
Name = opts.Name,
|
||||
Host = opts.Host,
|
||||
Port = opts.Port,
|
||||
Password = opts.Password
|
||||
};
|
||||
|
||||
config.Instances.Add(instance);
|
||||
SaveConfig(config, "redismanager.json");
|
||||
Console.WriteLine(Output.Green($"Instance '{opts.Name}' added successfully."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int RunUpdateInstance(UpdateInstanceOptions opts, Config config)
|
||||
{
|
||||
var instance = config.Instances.Find(i => i.Name == opts.Name);
|
||||
if (instance == null)
|
||||
{
|
||||
Console.WriteLine(Output.Red($"Instance '{opts.Name}' not found."));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(opts.Host))
|
||||
instance.Host = opts.Host;
|
||||
if (opts.Port != 6379)
|
||||
instance.Port = opts.Port;
|
||||
if (!string.IsNullOrEmpty(opts.Password))
|
||||
instance.Password = opts.Password;
|
||||
|
||||
SaveConfig(config, "redismanager.json");
|
||||
Console.WriteLine(Output.Green($"Instance '{opts.Name}' updated successfully."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int RunDeleteInstance(DeleteInstanceOptions opts, Config config)
|
||||
{
|
||||
var instance = config.Instances.Find(i => i.Name == opts.Name);
|
||||
if (instance == null)
|
||||
{
|
||||
Console.WriteLine(Output.Red($"Instance '{opts.Name}' not found."));
|
||||
return 1;
|
||||
}
|
||||
|
||||
config.Instances.Remove(instance);
|
||||
SaveConfig(config, "redismanager.json");
|
||||
Console.WriteLine(Output.Green($"Instance '{opts.Name}' deleted successfully."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void SaveConfig(Config config, string path)
|
||||
{
|
||||
var json = JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true });
|
||||
File.WriteAllText(path, json);
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Commands/InstanceOptions.cs
Normal file
13
Commands/InstanceOptions.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using CommandLine;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Base options for commands that require a Redis instance.
|
||||
/// </summary>
|
||||
public class InstanceOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Name of the Redis instance to use.")]
|
||||
public string Instance { get; set; }
|
||||
}
|
||||
}
|
||||
227
Commands/KeyCommands.cs
Normal file
227
Commands/KeyCommands.cs
Normal file
@ -0,0 +1,227 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedisManager.Utils;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
[Verb("exists", HelpText = "Check if key(s) exist.")]
|
||||
public class ExistsOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "keys", Min = 1, Required = true, HelpText = "Keys to check.")]
|
||||
public IEnumerable<string> Keys { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("expire", HelpText = "Set a timeout on a key.")]
|
||||
public class ExpireOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key to expire.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "seconds", Required = true, HelpText = "Expire time in seconds.")]
|
||||
public int Seconds { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("ttl", HelpText = "Get the time to live for a key.")]
|
||||
public class TtlOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key to check.")]
|
||||
public string Key { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("persist", HelpText = "Remove the expiration from a key.")]
|
||||
public class PersistOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key to persist.")]
|
||||
public string Key { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("rename", HelpText = "Rename a key.")]
|
||||
public class RenameOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Current key name.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "newkey", Required = true, HelpText = "New key name.")]
|
||||
public string NewKey { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("type", HelpText = "Get the type of a key.")]
|
||||
public class TypeOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key to check.")]
|
||||
public string Key { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("keys", HelpText = "Find all keys matching pattern.")]
|
||||
public class KeysOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "pattern", Required = true, HelpText = "Pattern to match.")]
|
||||
public string Pattern { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("scan", HelpText = "Incrementally iterate the keyspace.")]
|
||||
public class ScanOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Option("cursor", Required = false, HelpText = "Cursor to start from.")]
|
||||
public ulong Cursor { get; set; } = 0;
|
||||
[Option("pattern", Required = false, HelpText = "Pattern to match.")]
|
||||
public string Pattern { get; set; }
|
||||
[Option("count", Required = false, HelpText = "Count of keys to return.")]
|
||||
public int? Count { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
public static class KeyCommands
|
||||
{
|
||||
public static int RunExists(ExistsOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
|
||||
var count = db.KeyExists(keys);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Keys", "Exist Count" }, new List<string[]> { new[] { string.Join(", ", opts.Keys), count.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(count.ToString()));
|
||||
return 0;
|
||||
}
|
||||
public static int RunExpire(ExpireOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var ok = db.KeyExpire(opts.Key, TimeSpan.FromSeconds(opts.Seconds));
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Expire Set" }, new List<string[]> { new[] { opts.Key, ok ? "Yes" : "No" } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(ok ? "1" : "0"));
|
||||
return 0;
|
||||
}
|
||||
public static int RunTtl(TtlOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var ttl = db.KeyTimeToLive(opts.Key);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "TTL (s)" }, new List<string[]> { new[] { opts.Key, ttl?.TotalSeconds.ToString() ?? "-1" } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(ttl?.TotalSeconds.ToString() ?? "-1"));
|
||||
return 0;
|
||||
}
|
||||
public static int RunPersist(PersistOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var ok = db.KeyPersist(opts.Key);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Persisted" }, new List<string[]> { new[] { opts.Key, ok ? "Yes" : "No" } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(ok ? "1" : "0"));
|
||||
return 0;
|
||||
}
|
||||
public static int RunRename(RenameOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
db.KeyRename(opts.Key, opts.NewKey);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Old Key", "New Key", "Status" }, new List<string[]> { new[] { opts.Key, opts.NewKey, "Renamed" } });
|
||||
else
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
public static int RunType(TypeOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var type = db.KeyType(opts.Key);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Type" }, new List<string[]> { new[] { opts.Key, type.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(type.ToString()));
|
||||
return 0;
|
||||
}
|
||||
public static int RunKeys(KeysOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var keys = db.Execute("KEYS", opts.Pattern).ToString().Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = keys.Select(k => new[] { k }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Key" }, rows);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var key in keys)
|
||||
Console.WriteLine(Output.Green(key));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public static int RunScan(ScanOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var result = db.Execute("SCAN", opts.Cursor.ToString(),
|
||||
string.IsNullOrEmpty(opts.Pattern) ? null : "MATCH",
|
||||
string.IsNullOrEmpty(opts.Pattern) ? null : opts.Pattern,
|
||||
opts.Count.HasValue ? "COUNT" : null,
|
||||
opts.Count.HasValue ? opts.Count.Value.ToString() : null);
|
||||
var arr = (RedisResult[])result;
|
||||
var nextCursor = arr[0].ToString();
|
||||
var keys = ((RedisResult[])arr[1]).Select(x => x.ToString()).ToArray();
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = keys.Select(k => new[] { k }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Key" }, rows);
|
||||
RedisUtils.PrintTable(new[] { "Next Cursor" }, new List<string[]> { new[] { nextCursor } });
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var key in keys)
|
||||
Console.WriteLine(Output.Green(key));
|
||||
Console.WriteLine(Output.Yellow($"Next Cursor: {nextCursor}"));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
528
Commands/ListCommands.cs
Normal file
528
Commands/ListCommands.cs
Normal file
@ -0,0 +1,528 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedisManager.Utils;
|
||||
|
||||
using RedisManager.Commands;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
[Verb("lpush", HelpText = "Push elements to the left of a list.")]
|
||||
public class LPushOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "values", Min = 1, Required = true, HelpText = "Values to push.")]
|
||||
public IEnumerable<string> Values { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("rpush", HelpText = "Push elements to the right of a list.")]
|
||||
public class RPushOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "values", Min = 1, Required = true, HelpText = "Values to push.")]
|
||||
public IEnumerable<string> Values { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("lpop", HelpText = "Pop element from the left of a list.")]
|
||||
public class LPopOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
|
||||
public string Key { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("rpop", HelpText = "Pop element from the right of a list.")]
|
||||
public class RPopOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
|
||||
public string Key { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("lrange", HelpText = "Get range of elements from a list.")]
|
||||
public class LRangeOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "start", Required = true, HelpText = "Start index.")]
|
||||
public long Start { get; set; }
|
||||
[Value(2, MetaName = "stop", Required = true, HelpText = "Stop index.")]
|
||||
public long Stop { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("llen", HelpText = "Get the length of a list.")]
|
||||
public class LLenOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
|
||||
public string Key { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("lindex", HelpText = "Get element at index in a list.")]
|
||||
public class LIndexOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "index", Required = true, HelpText = "Element index.")]
|
||||
public long Index { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("lset", HelpText = "Set element at index in a list.")]
|
||||
public class LSetOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "index", Required = true, HelpText = "Element index.")]
|
||||
public long Index { get; set; }
|
||||
[Value(2, MetaName = "value", Required = true, HelpText = "New value.")]
|
||||
public string Value { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("lrem", HelpText = "Remove elements from a list.")]
|
||||
public class LRemOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "count", Required = true, HelpText = "Number of elements to remove (0=all, positive=from left, negative=from right).")]
|
||||
public long Count { get; set; }
|
||||
[Value(2, MetaName = "value", Required = true, HelpText = "Value to remove.")]
|
||||
public string Value { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("linsert", HelpText = "Insert element before or after pivot in a list.")]
|
||||
public class LInsertOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "position", Required = true, HelpText = "Position: BEFORE or AFTER.")]
|
||||
public string Position { get; set; }
|
||||
[Value(2, MetaName = "pivot", Required = true, HelpText = "Pivot element.")]
|
||||
public string Pivot { get; set; }
|
||||
[Value(3, MetaName = "value", Required = true, HelpText = "Value to insert.")]
|
||||
public string Value { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("ltrim", HelpText = "Trim list to specified range.")]
|
||||
public class LTrimOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "List key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "start", Required = true, HelpText = "Start index.")]
|
||||
public long Start { get; set; }
|
||||
[Value(2, MetaName = "stop", Required = true, HelpText = "Stop index.")]
|
||||
public long Stop { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("blpop", HelpText = "Blocking pop from the left of a list.")]
|
||||
public class BLPopOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "keys", Min = 1, Required = true, HelpText = "List keys.")]
|
||||
public IEnumerable<string> Keys { get; set; }
|
||||
[Value(1, MetaName = "timeout", Required = true, HelpText = "Timeout in seconds.")]
|
||||
public int Timeout { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("brpop", HelpText = "Blocking pop from the right of a list.")]
|
||||
public class BRPopOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "keys", Min = 1, Required = true, HelpText = "List keys.")]
|
||||
public IEnumerable<string> Keys { get; set; }
|
||||
[Value(1, MetaName = "timeout", Required = true, HelpText = "Timeout in seconds.")]
|
||||
public int Timeout { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("rpoplpush", HelpText = "Pop from right of source, push to left of destination.")]
|
||||
public class RPopLPushOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "source", Required = true, HelpText = "Source list key.")]
|
||||
public string Source { get; set; }
|
||||
[Value(1, MetaName = "destination", Required = true, HelpText = "Destination list key.")]
|
||||
public string Destination { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
public static class ListCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the LPUSH command to push elements to the left of a list.
|
||||
/// </summary>
|
||||
/// <param name="opts">The LPushOptions containing instance, key, and values.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunLPush(LPushOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var values = opts.Values.ToArray();
|
||||
var length = db.ListLeftPush(opts.Key, values.Select(v => (RedisValue)v).ToArray());
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = values.Select(v => new[] { v, "Pushed" }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Value", "Status" }, rows);
|
||||
RedisUtils.PrintTable(new[] { "Key", "New Length" }, new List<string[]> { new[] { opts.Key, length.ToString() } });
|
||||
}
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Pushed {values.Length} element(s), new length: {length}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the RPUSH command to push elements to the right of a list.
|
||||
/// </summary>
|
||||
/// <param name="opts">The RPushOptions containing instance, key, and values.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunRPush(RPushOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var values = opts.Values.ToArray();
|
||||
var length = db.ListRightPush(opts.Key, values.Select(v => (RedisValue)v).ToArray());
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = values.Select(v => new[] { v, "Pushed" }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Value", "Status" }, rows);
|
||||
RedisUtils.PrintTable(new[] { "Key", "New Length" }, new List<string[]> { new[] { opts.Key, length.ToString() } });
|
||||
}
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Pushed {values.Length} element(s), new length: {length}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the LPOP command to pop an element from the left of a list.
|
||||
/// </summary>
|
||||
/// <param name="opts">The LPopOptions containing instance and key.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunLPop(LPopOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var value = db.ListLeftPop(opts.Key);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Popped Value" }, new List<string[]> { new[] { opts.Key, value.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(value.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the RPOP command to pop an element from the right of a list.
|
||||
/// </summary>
|
||||
/// <param name="opts">The RPopOptions containing instance and key.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunRPop(RPopOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var value = db.ListRightPop(opts.Key);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Popped Value" }, new List<string[]> { new[] { opts.Key, value.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(value.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the LRANGE command to get a range of elements from a list.
|
||||
/// </summary>
|
||||
/// <param name="opts">The LRangeOptions containing instance, key, start, and stop.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunLRange(LRangeOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var values = db.ListRange(opts.Key, opts.Start, opts.Stop);
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = values.Select((v, i) => new[] { (opts.Start + i).ToString(), v.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Index", "Value" }, rows);
|
||||
}
|
||||
else
|
||||
foreach (var value in values)
|
||||
Console.WriteLine(Output.Green(value.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the LLEN command to get the length of a list.
|
||||
/// </summary>
|
||||
/// <param name="opts">The LLenOptions containing instance and key.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunLLen(LLenOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var length = db.ListLength(opts.Key);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Length" }, new List<string[]> { new[] { opts.Key, length.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(length.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the LINDEX command to get the element at a specific index in a list.
|
||||
/// </summary>
|
||||
/// <param name="opts">The LIndexOptions containing instance, key, and index.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunLIndex(LIndexOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var value = db.ListGetByIndex(opts.Key, opts.Index);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Index", "Value" }, new List<string[]> { new[] { opts.Key, opts.Index.ToString(), value.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(value.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the LSET command to set the element at a specific index in a list.
|
||||
/// </summary>
|
||||
/// <param name="opts">The LSetOptions containing instance, key, index, and value.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunLSet(LSetOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
db.ListSetByIndex(opts.Key, opts.Index, opts.Value);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Index", "New Value", "Status" }, new List<string[]> { new[] { opts.Key, opts.Index.ToString(), opts.Value, "OK" } });
|
||||
else
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the LREM command to remove elements from a list.
|
||||
/// </summary>
|
||||
/// <param name="opts">The LRemOptions containing instance, key, count, and value.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunLRem(LRemOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var count = db.ListRemove(opts.Key, opts.Value, opts.Count);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Value", "Removed Count" }, new List<string[]> { new[] { opts.Key, opts.Value, count.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Removed {count} element(s)"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the LINSERT command to insert an element before or after a pivot in a list.
|
||||
/// </summary>
|
||||
/// <param name="opts">The LInsertOptions containing instance, key, position, pivot, and value.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunLInsert(LInsertOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
long result;
|
||||
if (opts.Position.ToUpper() == "BEFORE")
|
||||
result = db.ListInsertBefore(opts.Key, opts.Pivot, opts.Value);
|
||||
else
|
||||
result = db.ListInsertAfter(opts.Key, opts.Pivot, opts.Value);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Position", "Pivot", "Value", "New Length" }, new List<string[]> { new[] { opts.Key, opts.Position, opts.Pivot, opts.Value, result.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green($"New length: {result}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the LTRIM command to trim a list to a specified range.
|
||||
/// </summary>
|
||||
/// <param name="opts">The LTrimOptions containing instance, key, start, and stop.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunLTrim(LTrimOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
db.ListTrim(opts.Key, opts.Start, opts.Stop);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Start", "Stop", "Status" }, new List<string[]> { new[] { opts.Key, opts.Start.ToString(), opts.Stop.ToString(), "OK" } });
|
||||
else
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the BLPOP command to perform a blocking pop from the left of a list.
|
||||
/// </summary>
|
||||
/// <param name="opts">The BLPopOptions containing instance, keys, and timeout.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunBLPop(BLPopOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var args = opts.Keys.Select(k => (RedisKey)k).Cast<object>().ToList();
|
||||
args.Add(opts.Timeout);
|
||||
var result = db.Execute("BLPOP", args.ToArray());
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
Console.WriteLine(Output.Yellow("Timeout - no elements available to pop."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
var arr = (RedisResult[])result;
|
||||
if (arr.Length == 2)
|
||||
{
|
||||
var key = arr[0]?.ToString();
|
||||
var value = arr[1]?.ToString();
|
||||
if (opts.Table)
|
||||
{
|
||||
RedisUtils.PrintTable(new[] { "Key", "Popped Value" }, new List<string[]> { new[] { key ?? "", value ?? "" } });
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(Output.Green($"{key}: {value}"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(Output.Yellow("Unexpected result format."));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the BRPOP command to perform a blocking pop from the right of a list.
|
||||
/// </summary>
|
||||
/// <param name="opts">The BRPopOptions containing instance, keys, and timeout.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunBRPop(BRPopOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var args = opts.Keys.Select(k => (RedisKey)k).Cast<object>().ToList();
|
||||
args.Add(opts.Timeout);
|
||||
var result = db.Execute("BRPOP", args.ToArray());
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
Console.WriteLine(Output.Yellow("Timeout - no elements available to pop."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
var arr = (RedisResult[])result;
|
||||
if (arr.Length == 2)
|
||||
{
|
||||
var key = arr[0]?.ToString();
|
||||
var value = arr[1]?.ToString();
|
||||
if (opts.Table)
|
||||
{
|
||||
RedisUtils.PrintTable(new[] { "Key", "Popped Value" }, new List<string[]> { new[] { key ?? "", value ?? "" } });
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(Output.Green($"{key}: {value}"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(Output.Yellow("Unexpected result format."));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the RPOPLPUSH command to pop from the right of the source and push to the left of the destination list.
|
||||
/// </summary>
|
||||
/// <param name="opts">The RPopLPushOptions containing instance, source, and destination.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunRPopLPush(RPopLPushOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var result = db.ListRightPopLeftPush(opts.Source, opts.Destination);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Source", "Destination", "Popped Value" }, new List<string[]> { new[] { opts.Source, opts.Destination, result.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(result.ToString()));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
99
Commands/ModuleCommands.cs
Normal file
99
Commands/ModuleCommands.cs
Normal file
@ -0,0 +1,99 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedisManager.Utils;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
[Verb("module-load", HelpText = "Load a Redis module.")]
|
||||
public class ModuleLoadOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "path", Required = true, HelpText = "Module path.")]
|
||||
public string Path { get; set; }
|
||||
[Value(1, MetaName = "args", HelpText = "Module arguments.")]
|
||||
public IEnumerable<string> Args { get; set; }
|
||||
}
|
||||
|
||||
[Verb("module-unload", HelpText = "Unload a Redis module.")]
|
||||
public class ModuleUnloadOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "name", Required = true, HelpText = "Module name.")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
[Verb("module-list", HelpText = "List loaded Redis modules.")]
|
||||
public class ModuleListOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
}
|
||||
|
||||
public static class ModuleCommands
|
||||
{
|
||||
public static int RunModuleLoad(ModuleLoadOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
try
|
||||
{
|
||||
var args = new List<object> { "LOAD", opts.Path };
|
||||
if (opts.Args != null)
|
||||
args.AddRange(opts.Args);
|
||||
var result = db.Execute("MODULE", args.ToArray());
|
||||
Console.WriteLine(Output.Green($"Module loaded: {result}"));
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(Output.Red($"Failed to load module: {ex.Message}"));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static int RunModuleUnload(ModuleUnloadOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
try
|
||||
{
|
||||
var result = db.Execute("MODULE", new object[] { "UNLOAD", opts.Name });
|
||||
Console.WriteLine(Output.Green($"Module unloaded: {result}"));
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(Output.Red($"Failed to unload module: {ex.Message}"));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static int RunModuleList(ModuleListOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
try
|
||||
{
|
||||
var result = db.Execute("MODULE", new object[] { "LIST" });
|
||||
Console.WriteLine(Output.Green($"Module list: {result}"));
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(Output.Red($"Failed to list modules: {ex.Message}"));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
119
Commands/PubSubCommands.cs
Normal file
119
Commands/PubSubCommands.cs
Normal file
@ -0,0 +1,119 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using RedisManager.Utils;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains command line options and implementations for Redis Pub/Sub operations.
|
||||
/// Provides functionality for PUBLISH, SUBSCRIBE, and UNSUBSCRIBE commands.
|
||||
/// </summary>
|
||||
[Verb("publish", HelpText = "Publish a message to a channel.")]
|
||||
public class PublishOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "channel", Required = true, HelpText = "Channel name.")]
|
||||
public string Channel { get; set; }
|
||||
[Value(1, MetaName = "message", Required = true, HelpText = "Message to publish.")]
|
||||
public string Message { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("subscribe", HelpText = "Subscribe to one or more channels.")]
|
||||
public class SubscribeOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "channels", Min = 1, Required = true, HelpText = "Channels to subscribe to.")]
|
||||
public IEnumerable<string> Channels { get; set; }
|
||||
}
|
||||
|
||||
[Verb("unsubscribe", HelpText = "Unsubscribe from one or more channels.")]
|
||||
public class UnsubscribeOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "channels", Min = 1, Required = true, HelpText = "Channels to unsubscribe from.")]
|
||||
public IEnumerable<string> Channels { get; set; }
|
||||
}
|
||||
|
||||
public static class PubSubCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the PUBLISH command to publish a message to a channel.
|
||||
/// </summary>
|
||||
/// <param name="opts">The PublishOptions containing instance, channel, and message.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunPublish(PublishOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var sub = redis.GetSubscriber();
|
||||
var count = sub.Publish(opts.Channel, opts.Message);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Channel", "Receivers" }, new List<string[]> { new[] { opts.Channel, count.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Published to {opts.Channel}, receivers: {count}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SUBSCRIBE command to subscribe to one or more channels.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SubscribeOptions containing instance and channels.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSubscribe(SubscribeOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var sub = redis.GetSubscriber();
|
||||
var channels = opts.Channels;
|
||||
Console.WriteLine(Output.Yellow($"Subscribed to: {string.Join(", ", channels)}. Press Ctrl+C to exit."));
|
||||
using var cts = new CancellationTokenSource();
|
||||
Console.CancelKeyPress += (s, e) => { e.Cancel = true; cts.Cancel(); };
|
||||
foreach (var channel in channels)
|
||||
{
|
||||
sub.Subscribe(channel, (ch, msg) =>
|
||||
{
|
||||
Console.WriteLine(Output.Cyan($"[{ch}] {msg}"));
|
||||
});
|
||||
}
|
||||
try
|
||||
{
|
||||
while (!cts.IsCancellationRequested)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
Console.WriteLine(Output.Yellow("Unsubscribed and exiting."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the UNSUBSCRIBE command to unsubscribe from one or more channels.
|
||||
/// </summary>
|
||||
/// <param name="opts">The UnsubscribeOptions containing instance and channels.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunUnsubscribe(UnsubscribeOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var sub = redis.GetSubscriber();
|
||||
foreach (var channel in opts.Channels)
|
||||
{
|
||||
sub.Unsubscribe(channel);
|
||||
Console.WriteLine(Output.Yellow($"Unsubscribed from {channel}"));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
156
Commands/ScriptingCommands.cs
Normal file
156
Commands/ScriptingCommands.cs
Normal file
@ -0,0 +1,156 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedisManager.Utils;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains command line options and implementations for Redis scripting operations.
|
||||
/// Provides functionality for EVAL, EVALSHA, SCRIPT LOAD, SCRIPT EXISTS, and SCRIPT FLUSH commands.
|
||||
/// </summary>
|
||||
[Verb("eval", HelpText = "Evaluate a Lua script.")]
|
||||
public class EvalOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "script", Required = true, HelpText = "Lua script.")]
|
||||
public string Script { get; set; }
|
||||
[Option("keys", Required = false, HelpText = "Comma-separated keys.")]
|
||||
public string Keys { get; set; }
|
||||
[Option("args", Required = false, HelpText = "Comma-separated arguments.")]
|
||||
public string Args { get; set; }
|
||||
}
|
||||
|
||||
[Verb("evalsha", HelpText = "Evaluate a Lua script by its SHA1 hash.")]
|
||||
public class EvalShaOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "sha1", Required = true, HelpText = "SHA1 hash.")]
|
||||
public string Sha1 { get; set; }
|
||||
[Option("keys", Required = false, HelpText = "Comma-separated keys.")]
|
||||
public string Keys { get; set; }
|
||||
[Option("args", Required = false, HelpText = "Comma-separated arguments.")]
|
||||
public string Args { get; set; }
|
||||
}
|
||||
|
||||
[Verb("scriptload", HelpText = "Load a Lua script into the script cache.")]
|
||||
public class ScriptLoadOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "script", Required = true, HelpText = "Lua script.")]
|
||||
public string Script { get; set; }
|
||||
}
|
||||
|
||||
[Verb("scriptexists", HelpText = "Check if scripts exist in the script cache.")]
|
||||
public class ScriptExistsOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "sha1s", Min = 1, Required = true, HelpText = "SHA1 hashes.")]
|
||||
public IEnumerable<string> Sha1s { get; set; }
|
||||
}
|
||||
|
||||
[Verb("scriptflush", HelpText = "Remove all scripts from the script cache.")]
|
||||
public class ScriptFlushOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
}
|
||||
|
||||
public static class ScriptingCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the EVAL command to evaluate a Lua script.
|
||||
/// </summary>
|
||||
/// <param name="opts">The EvalOptions containing instance, script, keys, and arguments.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunEval(EvalOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var keys = string.IsNullOrEmpty(opts.Keys) ? Array.Empty<RedisKey>() : opts.Keys.Split(',').Select(k => (RedisKey)k.Trim()).ToArray();
|
||||
var args = string.IsNullOrEmpty(opts.Args) ? Array.Empty<RedisValue>() : opts.Args.Split(',').Select(a => (RedisValue)a.Trim()).ToArray();
|
||||
var result = db.ScriptEvaluate(opts.Script, keys, args);
|
||||
Console.WriteLine(Output.Green(result.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the EVALSHA command to evaluate a Lua script by its SHA1 hash.
|
||||
/// </summary>
|
||||
/// <param name="opts">The EvalShaOptions containing instance, sha1, keys, and arguments.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunEvalSha(EvalShaOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var keys = string.IsNullOrEmpty(opts.Keys) ? Array.Empty<RedisKey>() : opts.Keys.Split(',').Select(k => (RedisKey)k.Trim()).ToArray();
|
||||
var args = string.IsNullOrEmpty(opts.Args) ? Array.Empty<RedisValue>() : opts.Args.Split(',').Select(a => (RedisValue)a.Trim()).ToArray();
|
||||
var result = db.ScriptEvaluate(opts.Sha1, keys, args);
|
||||
Console.WriteLine(Output.Green(result.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SCRIPT LOAD command to load a Lua script into the script cache.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ScriptLoadOptions containing instance and script.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunScriptLoad(ScriptLoadOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var server = redis.GetServer(redis.GetEndPoints().First());
|
||||
var sha1Bytes = server.ScriptLoad(opts.Script);
|
||||
// Convert byte[] to hex string
|
||||
var sha1 = BitConverter.ToString(sha1Bytes).Replace("-", string.Empty).ToLower();
|
||||
Console.WriteLine(Output.Green(sha1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SCRIPT EXISTS command to check if scripts exist in the script cache.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ScriptExistsOptions containing instance and SHA1 hashes.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunScriptExists(ScriptExistsOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var server = redis.GetServer(redis.GetEndPoints().First());
|
||||
foreach (var sha in opts.Sha1s)
|
||||
{
|
||||
var exists = server.ScriptExists(sha);
|
||||
Console.WriteLine(Output.Green($"{sha}: {(exists ? "YES" : "NO")}"));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SCRIPT FLUSH command to remove all scripts from the script cache.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ScriptFlushOptions containing instance.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunScriptFlush(ScriptFlushOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var server = redis.GetServer(redis.GetEndPoints().First());
|
||||
server.ScriptFlush();
|
||||
Console.WriteLine(Output.Green("Script cache flushed."));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
296
Commands/ServerCommands.cs
Normal file
296
Commands/ServerCommands.cs
Normal file
@ -0,0 +1,296 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedisManager.Utils;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains command line options and implementations for Redis server operations.
|
||||
/// Provides functionality for INFO, CONFIG, SAVE, BGSAVE, LASTSAVE, TIME, and PING commands.
|
||||
/// </summary>
|
||||
[Verb("info", HelpText = "Get information and statistics about the server.")]
|
||||
public class InfoOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Option("section", Required = false, HelpText = "Info section (server, clients, memory, etc.).")]
|
||||
public string Section { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("config", HelpText = "Get/set Redis configuration parameters.")]
|
||||
public class ConfigOptions
|
||||
{
|
||||
[Option("list-custom", Required = false, HelpText = "List all custom config parameters for this instance.")]
|
||||
public bool ListCustom { get; set; }
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Option("get", Required = false, HelpText = "Get configuration parameter.")]
|
||||
public string Get { get; set; }
|
||||
[Option("set", Required = false, HelpText = "Set configuration parameter (format: parameter value).")]
|
||||
public string Set { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("save", HelpText = "Synchronously save the dataset to disk.")]
|
||||
public class SaveOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("bgsave", HelpText = "Asynchronously save the dataset to disk.")]
|
||||
public class BgSaveOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("lastsave", HelpText = "Get the timestamp of the last successful save.")]
|
||||
public class LastSaveOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("time", HelpText = "Return the current server time.")]
|
||||
public class TimeOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("ping", HelpText = "Ping the server.")]
|
||||
public class PingOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
public static class ServerCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the INFO command to get information and statistics about the server.
|
||||
/// </summary>
|
||||
/// <param name="opts">The InfoOptions containing instance and section.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunInfo(InfoOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var info = db.Execute("INFO", string.IsNullOrEmpty(opts.Section) ? null : opts.Section).ToString();
|
||||
if (opts.Table)
|
||||
{
|
||||
var lines = info.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
var rows = lines.Select(l => l.Split(':', 2)).Where(parts => parts.Length == 2)
|
||||
.Select(parts => new[] { parts[0], parts[1] }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Parameter", "Value" }, rows);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(Output.Green(info));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the CONFIG command to get or set Redis configuration parameters.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ConfigOptions containing instance, get, set, and table options.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunConfig(ConfigOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
if (opts.ListCustom)
|
||||
{
|
||||
if (instance.CustomConfig != null && instance.CustomConfig.Count > 0)
|
||||
{
|
||||
RedisUtils.PrintTable(new[] { "Parameter", "Value" }, instance.CustomConfig.Select(kv => new[] { kv.Key, kv.Value }).ToList());
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(Output.Yellow("No custom config parameters set for this instance."));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(opts.Get))
|
||||
{
|
||||
var result = db.Execute("CONFIG", "GET", opts.Get);
|
||||
if (opts.Table)
|
||||
{
|
||||
var arr = (RedisResult[])result;
|
||||
var rows = new List<string[]>();
|
||||
for (int i = 0; i < arr.Length; i += 2)
|
||||
{
|
||||
if (i + 1 < arr.Length)
|
||||
rows.Add(new[] { arr[i].ToString(), arr[i + 1].ToString() });
|
||||
}
|
||||
RedisUtils.PrintTable(new[] { "Parameter", "Value" }, rows);
|
||||
}
|
||||
else
|
||||
{
|
||||
var arr = (RedisResult[])result;
|
||||
for (int i = 0; i < arr.Length; i += 2)
|
||||
{
|
||||
if (i + 1 < arr.Length)
|
||||
Console.WriteLine(Output.Green($"{arr[i]} {arr[i + 1]}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(opts.Set))
|
||||
{
|
||||
var parts = opts.Set.Split(' ', 2);
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
db.Execute("CONFIG", "SET", parts[0], parts[1]);
|
||||
|
||||
// Persist custom config in InstanceConfig and save config file
|
||||
var instanceConfig = config.Instances.FirstOrDefault(i => i.Name == opts.Instance);
|
||||
if (instanceConfig != null)
|
||||
{
|
||||
instanceConfig.CustomConfig[parts[0]] = parts[1];
|
||||
RedisManager.ConfigManager.SaveConfig(config);
|
||||
}
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Operation", "Status" }, new List<string[]> { new[] { "CONFIG SET", "OK" } });
|
||||
else
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(Output.Red("Invalid format. Use: parameter value"));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(Output.Red("Please specify --get or --set"));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SAVE command to synchronously save the dataset to disk.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SaveOptions containing instance and table option.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSave(SaveOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
db.Execute("SAVE");
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Operation", "Status" }, new List<string[]> { new[] { "SAVE", "OK" } });
|
||||
else
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the BGSAVE command to asynchronously save the dataset to disk.
|
||||
/// </summary>
|
||||
/// <param name="opts">The BgSaveOptions containing instance and table option.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunBgSave(BgSaveOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
db.Execute("BGSAVE");
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Operation", "Status" }, new List<string[]> { new[] { "BGSAVE", "Background save started" } });
|
||||
else
|
||||
Console.WriteLine(Output.Green("Background save started"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the LASTSAVE command to get the timestamp of the last successful save.
|
||||
/// </summary>
|
||||
/// <param name="opts">The LastSaveOptions containing instance and table option.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunLastSave(LastSaveOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var timestamp = db.Execute("LASTSAVE").ToString();
|
||||
var dateTime = DateTimeOffset.FromUnixTimeSeconds(long.Parse(timestamp));
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Last Save", "Timestamp", "DateTime" }, new List<string[]> { new[] { "Last Save", timestamp, dateTime.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green($"{timestamp} ({dateTime})"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the TIME command to return the current server time.
|
||||
/// </summary>
|
||||
/// <param name="opts">The TimeOptions containing instance and table option.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunTime(TimeOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var result = db.Execute("TIME");
|
||||
var arr = (RedisResult[])result;
|
||||
var timestamp = arr[0].ToString();
|
||||
var microseconds = arr[1].ToString();
|
||||
var dateTime = DateTimeOffset.FromUnixTimeSeconds(long.Parse(timestamp));
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Timestamp", "Microseconds", "DateTime" }, new List<string[]> { new[] { timestamp, microseconds, dateTime.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green($"{timestamp} {microseconds} ({dateTime})"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the PING command to ping the server.
|
||||
/// </summary>
|
||||
/// <param name="opts">The PingOptions containing instance and table option.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunPing(PingOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var response = db.Ping();
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Ping", "Response" }, new List<string[]> { new[] { "PING", response.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(response.ToString()));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
573
Commands/SetCommands.cs
Normal file
573
Commands/SetCommands.cs
Normal file
@ -0,0 +1,573 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedisManager.Utils;
|
||||
|
||||
using RedisManager.Commands;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
[Verb("sadd", HelpText = "Add members to a set.")]
|
||||
public class SAddOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "members", Min = 1, Required = true, HelpText = "Members to add.")]
|
||||
public IEnumerable<string> Members { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("srem", HelpText = "Remove members from a set.")]
|
||||
public class SRemOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "members", Min = 1, Required = true, HelpText = "Members to remove.")]
|
||||
public IEnumerable<string> Members { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("smembers", HelpText = "Get all members of a set.")]
|
||||
public class SMembersOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Set key.")]
|
||||
public string Key { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("scard", HelpText = "Get the number of members in a set.")]
|
||||
public class SCardOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Set key.")]
|
||||
public string Key { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("sismember", HelpText = "Check if a member exists in a set.")]
|
||||
public class SIsMemberOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "member", Required = true, HelpText = "Member to check.")]
|
||||
public string Member { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("srandmember", HelpText = "Get random member(s) from a set.")]
|
||||
public class SRandMemberOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Set key.")]
|
||||
public string Key { get; set; }
|
||||
[Option("count", Required = false, HelpText = "Number of random members to get.")]
|
||||
public int? Count { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("spop", HelpText = "Remove and return random member(s) from a set.")]
|
||||
public class SPopOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Set key.")]
|
||||
public string Key { get; set; }
|
||||
[Option("count", Required = false, HelpText = "Number of members to pop.")]
|
||||
public int? Count { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("sinter", HelpText = "Get intersection of multiple sets.")]
|
||||
public class SInterOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "keys", Min = 2, Required = true, HelpText = "Set keys.")]
|
||||
public IEnumerable<string> Keys { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("sunion", HelpText = "Get union of multiple sets.")]
|
||||
public class SUnionOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "keys", Min = 2, Required = true, HelpText = "Set keys.")]
|
||||
public IEnumerable<string> Keys { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("sdiff", HelpText = "Get difference of multiple sets.")]
|
||||
public class SDiffOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "keys", Min = 2, Required = true, HelpText = "Set keys.")]
|
||||
public IEnumerable<string> Keys { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("sinterstore", HelpText = "Store intersection of multiple sets.")]
|
||||
public class SInterStoreOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "destination", Required = true, HelpText = "Destination set key.")]
|
||||
public string Destination { get; set; }
|
||||
[Value(1, MetaName = "keys", Min = 2, Required = true, HelpText = "Source set keys.")]
|
||||
public IEnumerable<string> Keys { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("sunionstore", HelpText = "Store union of multiple sets.")]
|
||||
public class SUnionStoreOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "destination", Required = true, HelpText = "Destination set key.")]
|
||||
public string Destination { get; set; }
|
||||
[Value(1, MetaName = "keys", Min = 2, Required = true, HelpText = "Source set keys.")]
|
||||
public IEnumerable<string> Keys { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("sdiffstore", HelpText = "Store difference of multiple sets.")]
|
||||
public class SDiffStoreOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "destination", Required = true, HelpText = "Destination set key.")]
|
||||
public string Destination { get; set; }
|
||||
[Value(1, MetaName = "keys", Min = 2, Required = true, HelpText = "Source set keys.")]
|
||||
public IEnumerable<string> Keys { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("sscan", HelpText = "Scan set members.")]
|
||||
public class SScanOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "cursor", Required = true, HelpText = "Cursor position.")]
|
||||
public int Cursor { get; set; }
|
||||
[Option("match", Required = false, HelpText = "Pattern to match.")]
|
||||
public string Match { get; set; }
|
||||
[Option("count", Required = false, HelpText = "Count hint.")]
|
||||
public int? Count { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("smove", HelpText = "Move member from one set to another.")]
|
||||
public class SMoveOptions : InstanceOptions
|
||||
{
|
||||
[Value(0, MetaName = "source", Required = true, HelpText = "Source set key.")]
|
||||
public string Source { get; set; }
|
||||
[Value(1, MetaName = "destination", Required = true, HelpText = "Destination set key.")]
|
||||
public string Destination { get; set; }
|
||||
[Value(2, MetaName = "member", Required = true, HelpText = "Member to move.")]
|
||||
public string Member { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
public static class SetCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the SADD command to add members to a set.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SAddOptions containing instance, key, and members.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSAdd(SAddOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var members = opts.Members.ToArray();
|
||||
var count = db.SetAdd(opts.Key, members.Select(m => (RedisValue)m).ToArray());
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = members.Select(m => new[] { m, "Added" }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Member", "Status" }, rows);
|
||||
RedisUtils.PrintTable(new[] { "Key", "Added Count" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
|
||||
}
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Added {count} new member(s)"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SREM command to remove members from a set.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SRemOptions containing instance, key, and members.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSRem(SRemOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var members = opts.Members.ToArray();
|
||||
var count = db.SetRemove(opts.Key, members.Select(m => (RedisValue)m).ToArray());
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = members.Select(m => new[] { m, "Removed" }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Member", "Status" }, rows);
|
||||
RedisUtils.PrintTable(new[] { "Key", "Removed Count" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
|
||||
}
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Removed {count} member(s)"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SMEMBERS command to get all members of a set.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SMembersOptions containing instance and key.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSMembers(SMembersOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var members = db.SetMembers(opts.Key);
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = members.Select(m => new[] { m.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Member" }, rows);
|
||||
}
|
||||
else
|
||||
foreach (var member in members)
|
||||
Console.WriteLine(Output.Green(member.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SCARD command to get the number of members in a set.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SCardOptions containing instance and key.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSCard(SCardOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var count = db.SetLength(opts.Key);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Cardinality" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(count.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SISMEMBER command to check if a member exists in a set.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SIsMemberOptions containing instance, key, and member.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSIsMember(SIsMemberOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var exists = db.SetContains(opts.Key, opts.Member);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Member", "Exists" }, new List<string[]> { new[] { opts.Key, opts.Member, exists.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(exists.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SRANDMEMBER command to get random member(s) from a set.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SRandMemberOptions containing instance, key, and count.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSRandMember(SRandMemberOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
if (opts.Count.HasValue)
|
||||
{
|
||||
var members = db.SetRandomMembers(opts.Key, opts.Count.Value);
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = members.Select(m => new[] { m.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Random Member" }, rows);
|
||||
}
|
||||
else
|
||||
foreach (var member in members)
|
||||
Console.WriteLine(Output.Green(member.ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
var member = db.SetRandomMember(opts.Key);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Random Member" }, new List<string[]> { new[] { opts.Key, member.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(member.ToString()));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SPOP command to remove and return random member(s) from a set.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SPopOptions containing instance, key, and count.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSPop(SPopOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
if (opts.Count.HasValue)
|
||||
{
|
||||
var members = db.SetPop(opts.Key, opts.Count.Value);
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = members.Select(m => new[] { m.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Popped Member" }, rows);
|
||||
}
|
||||
else
|
||||
foreach (var member in members)
|
||||
Console.WriteLine(Output.Green(member.ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
var member = db.SetPop(opts.Key);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Popped Member" }, new List<string[]> { new[] { opts.Key, member.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(member.ToString()));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SINTER command to get the intersection of multiple sets.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SInterOptions containing instance and keys.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSInter(SInterOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
|
||||
var members = db.SetCombine(SetOperation.Intersect, keys);
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = members.Select(m => new[] { m.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Intersection Member" }, rows);
|
||||
}
|
||||
else
|
||||
foreach (var member in members)
|
||||
Console.WriteLine(Output.Green(member.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SUNION command to get the union of multiple sets.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SUnionOptions containing instance and keys.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSUnion(SUnionOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
|
||||
var members = db.SetCombine(SetOperation.Union, keys);
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = members.Select(m => new[] { m.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Union Member" }, rows);
|
||||
}
|
||||
else
|
||||
foreach (var member in members)
|
||||
Console.WriteLine(Output.Green(member.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SDIFF command to get the difference of multiple sets.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SDiffOptions containing instance and keys.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSDiff(SDiffOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
|
||||
var members = db.SetCombine(SetOperation.Difference, keys);
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = members.Select(m => new[] { m.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Difference Member" }, rows);
|
||||
}
|
||||
else
|
||||
foreach (var member in members)
|
||||
Console.WriteLine(Output.Green(member.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SINTERSTORE command to store the intersection of multiple sets.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SInterStoreOptions containing instance, destination, and keys.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSInterStore(SInterStoreOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
|
||||
var count = db.SetCombineAndStore(SetOperation.Intersect, opts.Destination, keys);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Destination", "Stored Count" }, new List<string[]> { new[] { opts.Destination, count.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Stored {count} member(s) in {opts.Destination}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SUNIONSTORE command to store the union of multiple sets.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SUnionStoreOptions containing instance, destination, and keys.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSUnionStore(SUnionStoreOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
|
||||
var count = db.SetCombineAndStore(SetOperation.Union, opts.Destination, keys);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Destination", "Stored Count" }, new List<string[]> { new[] { opts.Destination, count.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Stored {count} member(s) in {opts.Destination}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SDIFFSTORE command to store the difference of multiple sets.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SDiffStoreOptions containing instance, destination, and keys.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSDiffStore(SDiffStoreOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
|
||||
var count = db.SetCombineAndStore(SetOperation.Difference, opts.Destination, keys);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Destination", "Stored Count" }, new List<string[]> { new[] { opts.Destination, count.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Stored {count} member(s) in {opts.Destination}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SSCAN command to scan set members.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SScanOptions containing instance, key, cursor, and scan options.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSScan(SScanOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var args = new List<object> { opts.Cursor };
|
||||
if (!string.IsNullOrEmpty(opts.Match))
|
||||
{
|
||||
args.Add("MATCH");
|
||||
args.Add(opts.Match);
|
||||
}
|
||||
if (opts.Count.HasValue)
|
||||
{
|
||||
args.Add("COUNT");
|
||||
args.Add(opts.Count.Value);
|
||||
}
|
||||
|
||||
var result = db.Execute("SSCAN", opts.Key, args.ToArray());
|
||||
var arr = (RedisResult[])result;
|
||||
var newCursor = (int)arr[0];
|
||||
var members = (RedisResult[])arr[1];
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = members.Select(m => new[] { m.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Member" }, rows);
|
||||
RedisUtils.PrintTable(new[] { "Next Cursor" }, new List<string[]> { new[] { newCursor.ToString() } });
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var member in members)
|
||||
Console.WriteLine(Output.Green(member.ToString()));
|
||||
Console.WriteLine(Output.Yellow($"Next cursor: {newCursor}"));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SMOVE command to move a member from one set to another.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SMoveOptions containing instance, source, destination, and member.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunSMove(SMoveOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var moved = db.SetMove(opts.Source, opts.Destination, opts.Member);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Source", "Destination", "Member", "Moved" }, new List<string[]> { new[] { opts.Source, opts.Destination, opts.Member, moved.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(moved.ToString()));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
836
Commands/SortedSetCommands.cs
Normal file
836
Commands/SortedSetCommands.cs
Normal file
@ -0,0 +1,836 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedisManager.Utils;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains command line options and implementations for Redis sorted set operations.
|
||||
/// Provides functionality for ZADD, ZREM, ZRANGE, ZREVRANGE, ZRANGEBYSCORE, ZCARD, ZSCORE, ZRANK, ZREVRANK, ZINCRBY, ZREVRANGEBYSCORE, ZCOUNT, ZUNIONSTORE, ZINTERSTORE, ZSCAN, ZPOPMAX, ZPOPMIN, ZREMRANGEBYRANK, and ZREMRANGEBYSCORE commands.
|
||||
/// </summary>
|
||||
[Verb("zadd", HelpText = "Add members to a sorted set.")]
|
||||
public class ZAddOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "score", Required = true, HelpText = "Score for the member.")]
|
||||
public double Score { get; set; }
|
||||
[Value(2, MetaName = "member", Required = true, HelpText = "Member to add.")]
|
||||
public string Member { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zrem", HelpText = "Remove members from a sorted set.")]
|
||||
public class ZRemOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "members", Min = 1, Required = true, HelpText = "Members to remove.")]
|
||||
public IEnumerable<string> Members { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zrange", HelpText = "Get range of members by rank.")]
|
||||
public class ZRangeOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "start", Required = true, HelpText = "Start rank.")]
|
||||
public long Start { get; set; }
|
||||
[Value(2, MetaName = "stop", Required = true, HelpText = "Stop rank.")]
|
||||
public long Stop { get; set; }
|
||||
[Option("withscores", Required = false, HelpText = "Include scores in output.")]
|
||||
public bool WithScores { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zrevrange", HelpText = "Get range of members by rank (reverse order).")]
|
||||
public class ZRevRangeOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "start", Required = true, HelpText = "Start rank.")]
|
||||
public long Start { get; set; }
|
||||
[Value(2, MetaName = "stop", Required = true, HelpText = "Stop rank.")]
|
||||
public long Stop { get; set; }
|
||||
[Option("withscores", Required = false, HelpText = "Include scores in output.")]
|
||||
public bool WithScores { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zrangebyscore", HelpText = "Get range of members by score.")]
|
||||
public class ZRangeByScoreOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "min", Required = true, HelpText = "Minimum score.")]
|
||||
public double Min { get; set; }
|
||||
[Value(2, MetaName = "max", Required = true, HelpText = "Maximum score.")]
|
||||
public double Max { get; set; }
|
||||
[Option("withscores", Required = false, HelpText = "Include scores in output.")]
|
||||
public bool WithScores { get; set; }
|
||||
[Option("limit", Required = false, HelpText = "Limit results (offset count).")]
|
||||
public string Limit { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zcard", HelpText = "Get the number of members in a sorted set.")]
|
||||
public class ZCardOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zscore", HelpText = "Get the score of a member.")]
|
||||
public class ZScoreOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "member", Required = true, HelpText = "Member name.")]
|
||||
public string Member { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zrank", HelpText = "Get the rank of a member.")]
|
||||
public class ZRankOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "member", Required = true, HelpText = "Member name.")]
|
||||
public string Member { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zrevrank", HelpText = "Get the reverse rank of a member.")]
|
||||
public class ZRevRankOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "member", Required = true, HelpText = "Member name.")]
|
||||
public string Member { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zincrby", HelpText = "Increment the score of a member.")]
|
||||
public class ZIncrByOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "increment", Required = true, HelpText = "Score increment.")]
|
||||
public double Increment { get; set; }
|
||||
[Value(2, MetaName = "member", Required = true, HelpText = "Member name.")]
|
||||
public string Member { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zrevrangebyscore", HelpText = "Get range of members by score (reverse order).")]
|
||||
public class ZRevRangeByScoreOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "max", Required = true, HelpText = "Maximum score.")]
|
||||
public double Max { get; set; }
|
||||
[Value(2, MetaName = "min", Required = true, HelpText = "Minimum score.")]
|
||||
public double Min { get; set; }
|
||||
[Option("withscores", Required = false, HelpText = "Include scores in output.")]
|
||||
public bool WithScores { get; set; }
|
||||
[Option("limit", Required = false, HelpText = "Limit results (offset count).")]
|
||||
public string Limit { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zcount", HelpText = "Count members within a score range.")]
|
||||
public class ZCountOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "min", Required = true, HelpText = "Minimum score.")]
|
||||
public double Min { get; set; }
|
||||
[Value(2, MetaName = "max", Required = true, HelpText = "Maximum score.")]
|
||||
public double Max { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zunionstore", HelpText = "Store union of multiple sorted sets.")]
|
||||
public class ZUnionStoreOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "destination", Required = true, HelpText = "Destination sorted set key.")]
|
||||
public string Destination { get; set; }
|
||||
[Value(1, MetaName = "numkeys", Required = true, HelpText = "Number of source keys.")]
|
||||
public int NumKeys { get; set; }
|
||||
[Value(2, MetaName = "keys", Min = 1, Required = true, HelpText = "Source sorted set keys.")]
|
||||
public IEnumerable<string> Keys { get; set; }
|
||||
[Option("weights", Required = false, HelpText = "Weights for each source set.")]
|
||||
public IEnumerable<double> Weights { get; set; }
|
||||
[Option("aggregate", Required = false, HelpText = "Aggregation method (sum, min, max).")]
|
||||
public string Aggregate { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zinterstore", HelpText = "Store intersection of multiple sorted sets.")]
|
||||
public class ZInterStoreOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "destination", Required = true, HelpText = "Destination sorted set key.")]
|
||||
public string Destination { get; set; }
|
||||
[Value(1, MetaName = "numkeys", Required = true, HelpText = "Number of source keys.")]
|
||||
public int NumKeys { get; set; }
|
||||
[Value(2, MetaName = "keys", Min = 1, Required = true, HelpText = "Source sorted set keys.")]
|
||||
public IEnumerable<string> Keys { get; set; }
|
||||
[Option("weights", Required = false, HelpText = "Weights for each source set.")]
|
||||
public IEnumerable<double> Weights { get; set; }
|
||||
[Option("aggregate", Required = false, HelpText = "Aggregation method (sum, min, max).")]
|
||||
public string Aggregate { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zscan", HelpText = "Scan sorted set members.")]
|
||||
public class ZScanOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "cursor", Required = true, HelpText = "Cursor position.")]
|
||||
public int Cursor { get; set; }
|
||||
[Option("match", Required = false, HelpText = "Pattern to match.")]
|
||||
public string Match { get; set; }
|
||||
[Option("count", Required = false, HelpText = "Count hint.")]
|
||||
public int? Count { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zpopmax", HelpText = "Remove and return members with highest scores.")]
|
||||
public class ZPopMaxOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Option("count", Required = false, HelpText = "Number of members to pop.")]
|
||||
public int? Count { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zpopmin", HelpText = "Remove and return members with lowest scores.")]
|
||||
public class ZPopMinOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Option("count", Required = false, HelpText = "Number of members to pop.")]
|
||||
public int? Count { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zremrangebyrank", HelpText = "Remove members by rank range.")]
|
||||
public class ZRemRangeByRankOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "start", Required = true, HelpText = "Start rank.")]
|
||||
public long Start { get; set; }
|
||||
[Value(2, MetaName = "stop", Required = true, HelpText = "Stop rank.")]
|
||||
public long Stop { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("zremrangebyscore", HelpText = "Remove members by score range.")]
|
||||
public class ZRemRangeByScoreOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Sorted set key.")]
|
||||
public string Key { get; set; }
|
||||
[Value(1, MetaName = "min", Required = true, HelpText = "Minimum score.")]
|
||||
public double Min { get; set; }
|
||||
[Value(2, MetaName = "max", Required = true, HelpText = "Maximum score.")]
|
||||
public double Max { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
public static class SortedSetCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the ZADD command to add members to a sorted set.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZAddOptions containing instance, key, score, and member.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZAdd(ZAddOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var count = db.SortedSetAdd(opts.Key, opts.Member, opts.Score);
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
RedisUtils.PrintTable(new[] { "Key", "Added Count" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
|
||||
}
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Added {count} new member(s)"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZREM command to remove members from a sorted set.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZRemOptions containing instance, key, and members.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZRem(ZRemOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var members = opts.Members.ToArray();
|
||||
var count = db.SortedSetRemove(opts.Key, members.Select(m => (RedisValue)m).ToArray());
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = members.Select(m => new[] { m, "Removed" }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Member", "Status" }, rows);
|
||||
RedisUtils.PrintTable(new[] { "Key", "Removed Count" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
|
||||
}
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Removed {count} member(s)"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZRANGE command to get a range of members by rank.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZRangeOptions containing instance, key, start, and stop.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZRange(ZRangeOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
if (opts.WithScores)
|
||||
{
|
||||
var entries = db.SortedSetRangeByRankWithScores(opts.Key, opts.Start, opts.Stop, Order.Ascending);
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = entries.Select(e => new string[] { e.Element.ToString(), e.Score.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Member", "Score" }, rows);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var e in entries)
|
||||
Console.WriteLine(Output.Green($"{e.Element} {e.Score}"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var members = db.SortedSetRangeByRank(opts.Key, opts.Start, opts.Stop, Order.Ascending);
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = members.Select(m => new[] { m.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Member" }, rows);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var member in members)
|
||||
Console.WriteLine(Output.Green(member.ToString()));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZREVRANGE command to get a range of members by rank in reverse order.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZRevRangeOptions containing instance, key, start, and stop.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZRevRange(ZRevRangeOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
if (opts.WithScores)
|
||||
{
|
||||
var entries = db.SortedSetRangeByRankWithScores(opts.Key, opts.Start, opts.Stop, Order.Descending);
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = entries.Select(e => new string[] { e.Element.ToString(), e.Score.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Member", "Score" }, rows);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var e in entries)
|
||||
Console.WriteLine(Output.Green($"{e.Element} {e.Score}"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var members = db.SortedSetRangeByRank(opts.Key, opts.Start, opts.Stop, Order.Descending);
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = members.Select(m => new[] { m.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Member" }, rows);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var member in members)
|
||||
Console.WriteLine(Output.Green(member.ToString()));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZRANGEBYSCORE command to get a range of members by score.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZRangeByScoreOptions containing instance, key, min, and max.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZRangeByScore(ZRangeByScoreOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
if (opts.WithScores)
|
||||
{
|
||||
var entries = db.SortedSetRangeByScoreWithScores(opts.Key, opts.Min, opts.Max, Exclude.None, Order.Ascending);
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = entries.Select(e => new string[] { e.Element.ToString(), e.Score.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Member", "Score" }, rows);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var e in entries)
|
||||
Console.WriteLine(Output.Green($"{e.Element} {e.Score}"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var members = db.SortedSetRangeByScore(opts.Key, opts.Min, opts.Max, Exclude.None, Order.Ascending);
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = members.Select(m => new[] { m.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Member" }, rows);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var member in members)
|
||||
Console.WriteLine(Output.Green(member.ToString()));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZCARD command to get the number of members in a sorted set.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZCardOptions containing instance and key.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZCard(ZCardOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var count = db.SortedSetLength(opts.Key);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Cardinality" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(count.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZSCORE command to get the score of a member in a sorted set.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZScoreOptions containing instance, key, and member.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZScore(ZScoreOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var score = db.SortedSetScore(opts.Key, opts.Member);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Member", "Score" }, new List<string[]> { new[] { opts.Key, opts.Member, score?.ToString() ?? "[null]" } });
|
||||
else if (score.HasValue)
|
||||
Console.WriteLine(Output.Green(score.Value.ToString()));
|
||||
else
|
||||
Console.WriteLine(Output.Yellow("[null]"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZRANK command to get the rank of a member in a sorted set.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZRankOptions containing instance, key, and member.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZRank(ZRankOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var rank = db.SortedSetRank(opts.Key, opts.Member);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Member", "Rank" }, new List<string[]> { new[] { opts.Key, opts.Member, rank?.ToString() ?? "[null]" } });
|
||||
else if (rank.HasValue)
|
||||
Console.WriteLine(Output.Green(rank.Value.ToString()));
|
||||
else
|
||||
Console.WriteLine(Output.Yellow("[null]"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZREVRANK command to get the reverse rank of a member in a sorted set.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZRevRankOptions containing instance, key, and member.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZRevRank(ZRevRankOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var rank = db.SortedSetRank(opts.Key, opts.Member, Order.Descending);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Member", "Reverse Rank" }, new List<string[]> { new[] { opts.Key, opts.Member, rank?.ToString() ?? "[null]" } });
|
||||
else if (rank.HasValue)
|
||||
Console.WriteLine(Output.Green(rank.Value.ToString()));
|
||||
else
|
||||
Console.WriteLine(Output.Yellow("[null]"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZINCRBY command to increment the score of a member in a sorted set.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZIncrByOptions containing instance, key, increment, and member.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZIncrBy(ZIncrByOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var newScore = db.SortedSetIncrement(opts.Key, opts.Member, opts.Increment);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Member", "New Score" }, new List<string[]> { new[] { opts.Key, opts.Member, newScore.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(newScore.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZREVRANGEBYSCORE command to get a range of members by score in reverse order.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZRevRangeByScoreOptions containing instance, key, max, and min.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZRevRangeByScore(ZRevRangeByScoreOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
if (opts.WithScores)
|
||||
{
|
||||
var entries = db.SortedSetRangeByScoreWithScores(opts.Key, opts.Min, opts.Max, Exclude.None, Order.Descending);
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = entries.Select(e => new string[] { e.Element.ToString(), e.Score.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Member", "Score" }, rows);
|
||||
}
|
||||
else
|
||||
foreach (var entry in entries)
|
||||
Console.WriteLine(Output.Green($"{entry.Element}: {entry.Score}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
var members = db.SortedSetRangeByScore(opts.Key, opts.Min, opts.Max, Exclude.None, Order.Descending);
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = members.Select(m => new string[] { m.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Member" }, rows);
|
||||
}
|
||||
else
|
||||
foreach (var member in members)
|
||||
Console.WriteLine(Output.Green(member.ToString()));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZCOUNT command to count members within a score range in a sorted set.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZCountOptions containing instance, key, min, and max.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZCount(ZCountOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var count = db.SortedSetLengthByValue(opts.Key, opts.Min, opts.Max);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Count" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(count.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZUNIONSTORE command to store the union of multiple sorted sets.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZUnionStoreOptions containing instance, destination, numkeys, and keys.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZUnionStore(ZUnionStoreOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
|
||||
var count = db.SortedSetCombineAndStore(SetOperation.Union, opts.Destination, keys);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Destination", "Stored Count" }, new List<string[]> { new[] { opts.Destination, count.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Stored {count} member(s) in {opts.Destination}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZINTERSTORE command to store the intersection of multiple sorted sets.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZInterStoreOptions containing instance, destination, numkeys, and keys.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZInterStore(ZInterStoreOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
|
||||
var count = db.SortedSetCombineAndStore(SetOperation.Intersect, opts.Destination, keys);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Destination", "Stored Count" }, new List<string[]> { new[] { opts.Destination, count.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Stored {count} member(s) in {opts.Destination}"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZSCAN command to scan sorted set members.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZScanOptions containing instance, key, cursor, and scan options.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZScan(ZScanOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var result = db.SortedSetScan(opts.Key, opts.Match ?? "*", pageSize: opts.Count ?? 10, cursor: opts.Cursor);
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = result.Select(e => new[] { e.Element.ToString(), e.Score.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Member", "Score" }, rows);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var entry in result)
|
||||
Console.WriteLine(Output.Green($"{entry.Element}: {entry.Score}"));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZPOPMAX command to remove and return members with the highest scores.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZPopMaxOptions containing instance, key, and count.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZPopMax(ZPopMaxOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
if (opts.Count.HasValue)
|
||||
{
|
||||
var entries = db.SortedSetPop(opts.Key, opts.Count.Value);
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = entries.Select(e => new string[] { e.Element.ToString(), e.Score.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Popped Member", "Score" }, rows);
|
||||
}
|
||||
else
|
||||
foreach (var entry in entries)
|
||||
Console.WriteLine(Output.Green($"{entry.Element}: {entry.Score}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
var entry = db.SortedSetPop(opts.Key);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Popped Member", "Score" }, new List<string[]> { new[] { entry.Value.Element.ToString(), entry.Value.Score.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green($"{entry.Value.Element}: {entry.Value.Score}"));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZPOPMIN command to remove and return members with the lowest scores.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZPopMinOptions containing instance, key, and count.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZPopMin(ZPopMinOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
if (opts.Count.HasValue)
|
||||
{
|
||||
var entries = db.SortedSetPop(opts.Key, opts.Count.Value);
|
||||
if (opts.Table)
|
||||
{
|
||||
var rows = entries.Select(e => new string[] { e.Element.ToString(), e.Score.ToString() }).ToList();
|
||||
RedisUtils.PrintTable(new[] { "Popped Member", "Score" }, rows);
|
||||
}
|
||||
else
|
||||
foreach (var entry in entries)
|
||||
Console.WriteLine(Output.Green($"{entry.Element}: {entry.Score}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
var entry = db.SortedSetPop(opts.Key);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Popped Member", "Score" }, new List<string[]> { new[] { entry.Value.Element.ToString(), entry.Value.Score.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green($"{entry.Value.Element}: {entry.Value.Score}"));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZREMRANGEBYRANK command to remove members by rank range.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZRemRangeByRankOptions containing instance, key, start, and stop.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZRemRangeByRank(ZRemRangeByRankOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var count = db.SortedSetRemoveRangeByRank(opts.Key, opts.Start, opts.Stop);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Removed Count" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Removed {count} member(s)"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the ZREMRANGEBYSCORE command to remove members by score range.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ZRemRangeByScoreOptions containing instance, key, min, and max.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunZRemRangeByScore(ZRemRangeByScoreOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
var count = db.SortedSetRemoveRangeByScore(opts.Key, opts.Min, opts.Max);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Key", "Removed Count" }, new List<string[]> { new[] { opts.Key, count.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Removed {count} member(s)"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Commands/StatusCommand.cs
Normal file
46
Commands/StatusCommand.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using RedisManager.Utils;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Command line options for the status command.
|
||||
/// Used to check the connectivity and response time of a Redis instance.
|
||||
/// </summary>
|
||||
[Verb("status", HelpText = "Check status of a Redis instance.")]
|
||||
public class StatusOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the Redis instance to check.
|
||||
/// This must match a configured instance name in the configuration file.
|
||||
/// </summary>
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static class containing the implementation for the status command.
|
||||
/// Provides functionality to check Redis instance connectivity and response time.
|
||||
/// </summary>
|
||||
public static class StatusCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the status command to check Redis instance connectivity.
|
||||
/// Connects to the specified Redis instance and measures the ping response time.
|
||||
/// </summary>
|
||||
/// <param name="opts">The status command options containing the instance name</param>
|
||||
/// <param name="config">The RedisManager configuration containing instance details</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure)</returns>
|
||||
public static int RunStatus(StatusOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var pong = db.Ping();
|
||||
Console.WriteLine(Output.Green($"PONG: {pong.TotalMilliseconds}ms"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
154
Commands/StreamCommands.cs
Normal file
154
Commands/StreamCommands.cs
Normal file
@ -0,0 +1,154 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedisManager.Utils;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
[Verb("xadd", HelpText = "Add an entry to a stream.")]
|
||||
public class XAddOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "stream", Required = true, HelpText = "Stream key.")]
|
||||
public string Stream { get; set; }
|
||||
[Option("id", Required = false, HelpText = "Entry ID (default: *)")]
|
||||
public string Id { get; set; } = "*";
|
||||
[Option("fields", Required = true, HelpText = "Field-value pairs as field1:value1,field2:value2...")]
|
||||
public IEnumerable<string> Fields { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("xrange", HelpText = "Get entries from a stream within a range.")]
|
||||
public class XRangeOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "stream", Required = true, HelpText = "Stream key.")]
|
||||
public string Stream { get; set; }
|
||||
[Option("start", Required = false, HelpText = "Start ID (default: -)")]
|
||||
public string Start { get; set; } = "-";
|
||||
[Option("end", Required = false, HelpText = "End ID (default: +)")]
|
||||
public string End { get; set; } = "+";
|
||||
[Option("count", Required = false, HelpText = "Maximum number of entries")]
|
||||
public int? Count { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("xlen", HelpText = "Get the length of a stream.")]
|
||||
public class XLenOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "stream", Required = true, HelpText = "Stream key.")]
|
||||
public string Stream { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
[Verb("xdel", HelpText = "Remove entries from a stream.")]
|
||||
public class XDelOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "stream", Required = true, HelpText = "Stream key.")]
|
||||
public string Stream { get; set; }
|
||||
[Value(1, MetaName = "ids", Min = 1, Required = true, HelpText = "Entry IDs to delete.")]
|
||||
public IEnumerable<string> Ids { get; set; }
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
public static class StreamCommands
|
||||
{
|
||||
public static int RunXAdd(XAddOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
// Use XADD command directly
|
||||
var args = new List<RedisValue> { opts.Stream, opts.Id };
|
||||
foreach (var field in opts.Fields)
|
||||
{
|
||||
var parts = field.Split(':', 2);
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
args.Add(parts[0]);
|
||||
args.Add(parts[1]);
|
||||
}
|
||||
}
|
||||
var id = db.Execute("XADD", args.ToArray());
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Stream", "ID", "Fields" }, new List<string[]> { new[] { opts.Stream, id.ToString(), string.Join(", ", opts.Fields) } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(id.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int RunXRange(XRangeOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
// Use XRANGE command directly
|
||||
var args = new List<RedisValue> { opts.Stream, opts.Start, opts.End };
|
||||
if (opts.Count.HasValue)
|
||||
{
|
||||
args.Add("COUNT");
|
||||
args.Add(opts.Count.Value);
|
||||
}
|
||||
var result = db.Execute("XRANGE", args.ToArray());
|
||||
|
||||
if (opts.Table)
|
||||
{
|
||||
Console.WriteLine(Output.Yellow("Table output not supported for XRANGE yet"));
|
||||
}
|
||||
else
|
||||
Console.WriteLine(Output.Green(result.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int RunXLen(XLenOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
// Use XLEN command directly
|
||||
var length = db.Execute("XLEN", opts.Stream);
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Stream", "Length" }, new List<string[]> { new[] { opts.Stream, length.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green(length.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int RunXDel(XDelOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
|
||||
// Use XDEL command directly
|
||||
var args = new List<RedisValue> { opts.Stream };
|
||||
args.AddRange(opts.Ids.Select(id => (RedisValue)id));
|
||||
var count = db.Execute("XDEL", args.ToArray());
|
||||
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Stream", "Deleted Count" }, new List<string[]> { new[] { opts.Stream, count.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Deleted {count} entry(ies)"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
201
Commands/StringCommands.cs
Normal file
201
Commands/StringCommands.cs
Normal file
@ -0,0 +1,201 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using RedisManager.Utils;
|
||||
|
||||
using RedisManager.Commands;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Command line options for the GET command.
|
||||
/// Used to retrieve the value of a Redis key.
|
||||
/// </summary>
|
||||
[Verb("get", HelpText = "Get value of a key.")]
|
||||
public class GetOptions : InstanceOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the key name to retrieve the value for.
|
||||
/// </summary>
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
|
||||
public string Key { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to output the result in table format.
|
||||
/// </summary>
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command line options for the SET command.
|
||||
/// Used to set the value of a Redis key with optional TTL.
|
||||
/// </summary>
|
||||
[Verb("set", HelpText = "Set value of a key.")]
|
||||
public class SetOptions : InstanceOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the key name to set the value for.
|
||||
/// </summary>
|
||||
[Value(0, MetaName = "key", Required = true, HelpText = "Key name.")]
|
||||
public string Key { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value to store in the key.
|
||||
/// </summary>
|
||||
[Value(1, MetaName = "value", Required = true, HelpText = "Value.")]
|
||||
public string Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time to live in seconds for the key.
|
||||
/// </summary>
|
||||
[Option("ttl", Required = false, HelpText = "Time to live in seconds.")]
|
||||
public int? TTL { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command line options for the DEL command.
|
||||
/// Used to delete one or more Redis keys with optional confirmation.
|
||||
/// </summary>
|
||||
[Verb("del", HelpText = "Delete one or more keys.")]
|
||||
public class DelOptions : InstanceOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets whether to skip the confirmation prompt.
|
||||
/// </summary>
|
||||
[Option("yes", Required = false, HelpText = "Skip confirmation.")]
|
||||
public bool Yes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the collection of keys to delete.
|
||||
/// </summary>
|
||||
[Value(0, MetaName = "keys", Min = 1, Required = true, HelpText = "Key(s) to delete.")]
|
||||
public IEnumerable<string> Keys { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to output the result in table format.
|
||||
/// </summary>
|
||||
[Option("table", Required = false, HelpText = "Output as table.")]
|
||||
public bool Table { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static class containing implementations for basic Redis string operations.
|
||||
/// Provides functionality for GET, SET, and DEL commands with various output formats.
|
||||
/// </summary>
|
||||
public static class StringCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the GET command to retrieve a value from Redis.
|
||||
/// Supports table output format and JSON pretty-printing.
|
||||
/// </summary>
|
||||
/// <param name="opts">The GET command options</param>
|
||||
/// <param name="config">The RedisManager configuration</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure)</returns>
|
||||
public static int RunGet(GetOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var value = db.StringGet(opts.Key);
|
||||
if (opts.Table)
|
||||
{
|
||||
RedisUtils.PrintTable(new[] { "Key", "Value" }, new List<string[]> { new[] { opts.Key, value.IsNull ? "[null]" : value.ToString() } });
|
||||
}
|
||||
else if (value.IsNull)
|
||||
Console.WriteLine(Output.Yellow($"[null]"));
|
||||
else
|
||||
{
|
||||
// Try pretty-print JSON
|
||||
string str = value.ToString();
|
||||
if (IsJson(str))
|
||||
{
|
||||
try
|
||||
{
|
||||
var doc = JsonDocument.Parse(str);
|
||||
var pretty = JsonSerializer.Serialize(doc, new JsonSerializerOptions { WriteIndented = true });
|
||||
Console.WriteLine(Output.Green(pretty));
|
||||
}
|
||||
catch { Console.WriteLine(Output.Green(str)); }
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(Output.Green(str));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the SET command to store a value in Redis.
|
||||
/// Supports optional TTL (time to live) for the key.
|
||||
/// </summary>
|
||||
/// <param name="opts">The SET command options</param>
|
||||
/// <param name="config">The RedisManager configuration</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure)</returns>
|
||||
public static int RunSet(SetOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
bool ok;
|
||||
if (opts.TTL.HasValue)
|
||||
ok = db.StringSet(opts.Key, opts.Value, TimeSpan.FromSeconds(opts.TTL.Value));
|
||||
else
|
||||
ok = db.StringSet(opts.Key, opts.Value);
|
||||
if (ok)
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
else
|
||||
Console.WriteLine(Output.Red("Failed to set key."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the DEL command to delete one or more keys from Redis.
|
||||
/// Supports confirmation prompts and table output format.
|
||||
/// </summary>
|
||||
/// <param name="opts">The DEL command options</param>
|
||||
/// <param name="config">The RedisManager configuration</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure)</returns>
|
||||
public static int RunDel(DelOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
|
||||
|
||||
if (!opts.Yes)
|
||||
{
|
||||
Console.WriteLine(Output.Yellow($"Are you sure you want to delete {keys.Length} key(s)? (y/N)"));
|
||||
var response = Console.ReadLine()?.ToLower();
|
||||
if (response != "y" && response != "yes")
|
||||
{
|
||||
Console.WriteLine(Output.Red("Operation cancelled."));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
var count = db.KeyDelete(keys);
|
||||
if (opts.Table)
|
||||
RedisUtils.PrintTable(new[] { "Keys", "Deleted" }, new List<string[]> { new[] { string.Join(", ", opts.Keys), count.ToString() } });
|
||||
else
|
||||
Console.WriteLine(Output.Green($"Deleted {count} key(s)"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a string appears to be JSON formatted.
|
||||
/// Checks if the string starts and ends with curly braces or square brackets.
|
||||
/// </summary>
|
||||
/// <param name="str">The string to check for JSON formatting</param>
|
||||
/// <returns>True if the string appears to be JSON, false otherwise</returns>
|
||||
private static bool IsJson(string str)
|
||||
{
|
||||
str = str.Trim();
|
||||
return (str.StartsWith("{") && str.EndsWith("}")) || (str.StartsWith("[") && str.EndsWith("]"));
|
||||
}
|
||||
}
|
||||
}
|
||||
134
Commands/TransactionCommands.cs
Normal file
134
Commands/TransactionCommands.cs
Normal file
@ -0,0 +1,134 @@
|
||||
using CommandLine;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedisManager.Utils;
|
||||
|
||||
namespace RedisManager.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains command line options and implementations for Redis transaction operations.
|
||||
/// Provides functionality for MULTI, EXEC, DISCARD, WATCH, and UNWATCH commands.
|
||||
/// </summary>
|
||||
[Verb("multi", HelpText = "Mark the start of a transaction block.")]
|
||||
public class MultiOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
}
|
||||
|
||||
[Verb("exec", HelpText = "Execute all commands issued after MULTI.")]
|
||||
public class ExecOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
}
|
||||
|
||||
[Verb("discard", HelpText = "Flush all commands issued after MULTI.")]
|
||||
public class DiscardOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
}
|
||||
|
||||
[Verb("watch", HelpText = "Watch the given keys to determine execution of the MULTI/EXEC block.")]
|
||||
public class WatchOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
[Value(0, MetaName = "keys", Min = 1, Required = true, HelpText = "Keys to watch.")]
|
||||
public IEnumerable<string> Keys { get; set; }
|
||||
}
|
||||
|
||||
[Verb("unwatch", HelpText = "Forget about all watched keys.")]
|
||||
public class UnwatchOptions
|
||||
{
|
||||
[Option('i', "instance", Required = true, HelpText = "Instance name.")]
|
||||
public string Instance { get; set; }
|
||||
}
|
||||
|
||||
public static class TransactionCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the MULTI command to mark the start of a transaction block.
|
||||
/// </summary>
|
||||
/// <param name="opts">The MultiOptions containing instance.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunMulti(MultiOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
db.Execute("MULTI");
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the EXEC command to execute all commands issued after MULTI.
|
||||
/// </summary>
|
||||
/// <param name="opts">The ExecOptions containing instance.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunExec(ExecOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var result = db.Execute("EXEC");
|
||||
Console.WriteLine(Output.Green(result.ToString()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the DISCARD command to flush all commands issued after MULTI.
|
||||
/// </summary>
|
||||
/// <param name="opts">The DiscardOptions containing instance.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunDiscard(DiscardOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
db.Execute("DISCARD");
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the WATCH command to watch the given keys for a transaction block.
|
||||
/// </summary>
|
||||
/// <param name="opts">The WatchOptions containing instance and keys.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunWatch(WatchOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
var keys = opts.Keys.Select(k => (RedisKey)k).ToArray();
|
||||
db.Execute("WATCH", keys);
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the UNWATCH command to forget about all watched keys.
|
||||
/// </summary>
|
||||
/// <param name="opts">The UnwatchOptions containing instance.</param>
|
||||
/// <param name="config">The RedisManager configuration.</param>
|
||||
/// <returns>Exit code (0 for success, non-zero for failure).</returns>
|
||||
public static int RunUnwatch(UnwatchOptions opts, Config config)
|
||||
{
|
||||
var instance = RedisUtils.GetInstance(config, opts.Instance);
|
||||
using var redis = RedisUtils.ConnectRedis(instance);
|
||||
var db = redis.GetDatabase();
|
||||
db.Execute("UNWATCH");
|
||||
Console.WriteLine(Output.Green("OK"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user