Files
RedisManager/Commands/SetCommands.cs
GuilhermeStrice f37078157d add project
2025-07-09 19:31:34 +01:00

573 lines
26 KiB
C#

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;
}
}
}