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

242 lines
11 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 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
};
}
}
}