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

583 lines
27 KiB
C#

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