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