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 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 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 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 { 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 { new[] { keys[0], count.ToString() } }); else RedisUtils.PrintTable(new[] { "Keys", "Combined Cardinality" }, new List { 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 { new[] { opts.DestKey, "OK" } }); } else Console.WriteLine(Output.Green("OK")); return 0; } } }