Commit 94213e9a authored by Marc Gravell's avatar Marc Gravell

Merge branch 'wjdavis5-AddGeoAdd'

parents bed3c2bc b2622101
......@@ -134,6 +134,7 @@ public void CheckFailureRecovered()
ClearAmbientFailures();
}
}
#endif
[Test]
public void TryGetAzureRoleInstanceIdNoThrow()
......
using NUnit.Framework;
using System;
using System.IO;
using System.Linq;
namespace StackExchange.Redis.Tests
{
public class AzureTestAttribute : TestAttribute
{
}
[TestFixture]
public class GeoTests : TestBase
{
private ConnectionMultiplexer Create()
{
string name, password;
GetAzureCredentials(out name, out password);
var options = new ConfigurationOptions();
options.EndPoints.Add(name + ".redis.cache.windows.net");
options.Ssl = true;
options.ConnectTimeout = 5000;
options.Password = password;
options.TieBreaker = "";
var log = new StringWriter();
var conn = ConnectionMultiplexer.Connect(options, log);
var s = log.ToString();
Console.WriteLine(s);
return conn;
}
public const int Db = 0;
public static GeoEntry
palermo = new GeoEntry(13.361389, 38.115556, "Palermo"),
catania = new GeoEntry(15.087269, 37.502669, "Catania"),
agrigento = new GeoEntry(13.5765, 37.311, "Agrigento"),
cefal = new GeoEntry(14.0188, 38.0084, "Cefal");
public static GeoEntry[] all = { palermo, catania, agrigento , cefal };
[AzureTest]
public void GeoAdd()
{
using (var conn = Create())
{
var db = conn.GetDatabase(Db);
RedisKey key = Me();
db.KeyDelete(key);
// add while not there
Assert.IsTrue(db.GeoAdd(key, cefal.Longitude, cefal.Latitude, cefal.Member));
Assert.AreEqual(2, db.GeoAdd(key, new GeoEntry[] { palermo, catania }));
Assert.IsTrue(db.GeoAdd(key, agrigento));
// now add again
Assert.IsFalse(db.GeoAdd(key, cefal.Longitude, cefal.Latitude, cefal.Member));
Assert.AreEqual(0, db.GeoAdd(key, new GeoEntry[] { palermo, catania }));
Assert.IsFalse(db.GeoAdd(key, agrigento));
}
}
[AzureTest]
public void GetDistance()
{
using (var conn = Create())
{
var db = conn.GetDatabase(Db);
RedisKey key = Me();
db.KeyDelete(key);
db.GeoAdd(key, all);
var val = db.GeoDistance(key, "Palermo", "Catania", GeoUnit.Meters);
Assert.IsTrue(val.HasValue);
var rounded = Math.Round(val.Value, 10);
Assert.AreEqual(166274.1516, val);
val = db.GeoDistance(key, "Palermo", "Nowhere", GeoUnit.Meters);
Assert.IsFalse(val.HasValue);
}
}
[AzureTest]
public void GeoHash()
{
using (var conn = Create())
{
var db = conn.GetDatabase(Db);
RedisKey key = Me();
db.KeyDelete(key);
db.GeoAdd(key, all);
var hashes = db.GeoHash(key, new RedisValue[] { palermo.Member, "Nowhere", agrigento.Member });
Assert.AreEqual(3, hashes.Length);
Assert.AreEqual("sqc8b49rny0", hashes[0]);
Assert.IsNull(hashes[1]);
Assert.AreEqual("sq9skbq0760", hashes[2]);
var hash = db.GeoHash(key, "Palermo");
Assert.AreEqual("sqc8b49rny0", hash);
hash = db.GeoHash(key, "Nowhere");
Assert.IsNull(hash);
}
}
[AzureTest]
public void GeoGetPosition()
{
using (var conn = Create())
{
var db = conn.GetDatabase(Db);
RedisKey key = Me();
db.KeyDelete(key);
db.GeoAdd(key, all);
var pos = db.GeoPosition(key, palermo.Member);
Assert.IsTrue(pos.HasValue);
Assert.AreEqual(Math.Round(palermo.Longitude, 6), Math.Round(pos.Value.Longitude, 6));
Assert.AreEqual(Math.Round(palermo.Latitude, 6), Math.Round(pos.Value.Latitude, 6));
pos = db.GeoPosition(key, "Nowhere");
Assert.IsFalse(pos.HasValue);
}
}
[AzureTest]
public void GeoRemove()
{
using (var conn = Create())
{
var db = conn.GetDatabase(Db);
RedisKey key = Me();
db.KeyDelete(key);
db.GeoAdd(key, all);
var pos = db.GeoPosition(key, "Palermo");
Assert.IsTrue(pos.HasValue);
Assert.IsFalse(db.GeoRemove(key, "Nowhere"));
Assert.IsTrue(db.GeoRemove(key, "Palermo"));
Assert.IsFalse(db.GeoRemove(key, "Palermo"));
pos = db.GeoPosition(key, "Palermo");
Assert.IsFalse(pos.HasValue);
}
}
[AzureTest]
public void GeoRadius()
{
using (var conn = Create())
{
var db = conn.GetDatabase(Db);
RedisKey key = Me();
db.KeyDelete(key);
db.GeoAdd(key, all);
var results = db.GeoRadius(key, cefal.Member, 60, GeoUnit.Miles, 2, Order.Ascending);
Assert.AreEqual(2, results.Length);
Assert.AreEqual(results[0].Member, cefal.Member);
Assert.AreEqual(0, results[0].Distance.Value);
Assert.AreEqual(Math.Round(results[0].Position.Value.Longitude, 5), Math.Round(cefal.Position.Longitude, 5));
Assert.AreEqual(Math.Round(results[0].Position.Value.Latitude, 5), Math.Round(cefal.Position.Latitude, 5));
Assert.IsFalse(results[0].Hash.HasValue);
Assert.AreEqual(results[1].Member, palermo.Member);
Assert.AreEqual(Math.Round(36.5319, 6), Math.Round(results[1].Distance.Value, 6));
Assert.AreEqual(Math.Round(results[1].Position.Value.Longitude, 5), Math.Round(palermo.Position.Longitude, 5));
Assert.AreEqual(Math.Round(results[1].Position.Value.Latitude, 5), Math.Round(palermo.Position.Latitude, 5));
Assert.IsFalse(results[1].Hash.HasValue);
results = db.GeoRadius(key, cefal.Member, 60, GeoUnit.Miles, 2, Order.Ascending, GeoRadiusOptions.None);
Assert.AreEqual(2, results.Length);
Assert.AreEqual(results[0].Member, cefal.Member);
Assert.IsFalse(results[0].Position.HasValue);
Assert.IsFalse(results[0].Distance.HasValue);
Assert.IsFalse(results[0].Hash.HasValue);
Assert.AreEqual(results[1].Member, palermo.Member);
Assert.IsFalse(results[1].Position.HasValue);
Assert.IsFalse(results[1].Distance.HasValue);
Assert.IsFalse(results[1].Hash.HasValue);
}
}
}
}
using NUnit.Framework;
using System.IO;
namespace StackExchange.Redis.Tests
{
......
using System;
namespace StackExchange.Redis
{
/// <summary>
/// GeoRadius command options.
/// </summary>
[Flags]
public enum GeoRadiusOptions
{
/// <summary>
/// No Options
/// </summary>
None = 0,
/// <summary>
/// Redis will return the coordinates of any results.
/// </summary>
WithCoordinates = 1,
/// <summary>
/// Redis will return the distance from center for all results.
/// </summary>
WithDistance = 2,
/// <summary>
/// Redis will return the geo hash value as an integer. (This is the score in the sorted set)
/// </summary>
WithGeoHash = 4,
/// <summary>
/// Populates the commonly used values from the entry (the integer hash is not returned as it is not commonly useful)
/// </summary>
Default = WithCoordinates | GeoRadiusOptions.WithDistance
}
/// <summary>
/// The result of a GeoRadius command.
/// </summary>
public struct GeoRadiusResult
{
/// <summary>
/// Indicate the member being represented
/// </summary>
public override string ToString() => Member.ToString();
/// <summary>
/// The matched member.
/// </summary>
public RedisValue Member { get; }
/// <summary>
/// The distance of the matched member from the center of the geo radius command.
/// </summary>
public double? Distance { get; }
/// <summary>
/// The hash value of the matched member as an integer. (The key in the sorted set)
/// </summary>
/// <remarks>Note that this is not the same as the hash returned from GeoHash</remarks>
public long? Hash { get; }
/// <summary>
/// The coordinates of the matched member.
/// </summary>
public GeoPosition? Position { get; }
/// <summary>
/// Returns a new GeoRadiusResult
/// </summary>
internal GeoRadiusResult(RedisValue member, double? distance, long? hash, GeoPosition? position)
{
Member = member;
Distance = distance;
Hash = hash;
Position = position;
}
}
/// <summary>
/// Describes the longitude and latitude of a GeoEntry
/// </summary>
public struct GeoPosition : IEquatable<GeoPosition>
{
internal static string GetRedisUnit(GeoUnit unit)
{
switch (unit)
{
case GeoUnit.Meters: return "m";
case GeoUnit.Kilometers: return "km";
case GeoUnit.Miles: return "mi";
case GeoUnit.Feet: return "ft";
default:
throw new ArgumentOutOfRangeException(nameof(unit));
}
}
/// <summary>
/// The Latitude of the GeoPosition
/// </summary>
public double Latitude { get; }
/// <summary>
/// The Logitude of the GeoPosition
/// </summary>
public double Longitude { get; }
/// <summary>
/// Creates a new GeoPosition
/// </summary>
/// <param name="longitude"></param>
/// <param name="latitude"></param>
public GeoPosition(double longitude, double latitude)
{
Longitude = longitude;
Latitude = latitude;
}
/// <summary>
/// See Object.ToString()
/// </summary>
public override string ToString()
{
return string.Format("{0} {1}", Longitude, Latitude);
}
/// <summary>
/// See Object.GetHashCode()
/// Diagonals not an issue in the case of lat/long
/// </summary>
public override int GetHashCode()
{
// diagonals not an issue in the case of lat/long
return Longitude.GetHashCode() ^ Latitude.GetHashCode();
}
/// <summary>
/// Compares two values for equality
/// </summary>
public override bool Equals(object obj)
{
return obj is GeoPosition && Equals((GeoPosition)obj);
}
/// <summary>
/// Compares two values for equality
/// </summary>
public bool Equals(GeoPosition value)
{
return this == value;
}
/// <summary>
/// Compares two values for equality
/// </summary>
public static bool operator ==(GeoPosition x, GeoPosition y)
{
return x.Longitude == y.Longitude && x.Latitude == y.Latitude;
}
/// <summary>
/// Compares two values for non-equality
/// </summary>
public static bool operator !=(GeoPosition x, GeoPosition y)
{
return x.Longitude != y.Longitude || x.Latitude != y.Latitude;
}
}
/// <summary>
/// Describes a GeoEntry element with the corresponding value
/// GeoEntries are stored in redis as SortedSetEntries
/// </summary>
public struct GeoEntry : IEquatable<GeoEntry>
{
/// <summary>
/// The name of the geo entry
/// </summary>
public RedisValue Member { get; }
/// <summary>
/// Describes the longitude and latitude of a GeoEntry
/// </summary>
public GeoPosition Position { get; }
/// <summary>
/// Initializes a GeoEntry value
/// </summary>
public GeoEntry(double longitude, double latitude, RedisValue member)
{
Member = member;
Position = new GeoPosition(longitude, latitude);
}
/// <summary>
/// The longitude of the geo entry
/// </summary>
public double Longitude => Position.Longitude;
/// <summary>
/// The latitude of the geo entry
/// </summary>
public double Latitude => Position.Latitude;
/// <summary>
/// See Object.ToString()
/// </summary>
public override string ToString()
{
return $"({Longitude},{Latitude})={Member}";
}
/// <summary>
/// See Object.GetHashCode()
/// </summary>
public override int GetHashCode()
{
return Position.GetHashCode() ^ Member.GetHashCode();
}
/// <summary>
/// Compares two values for equality
/// </summary>
public override bool Equals(object obj)
{
return obj is GeoEntry && Equals((GeoEntry)obj);
}
/// <summary>
/// Compares two values for equality
/// </summary>
public bool Equals(GeoEntry value)
{
return this == value;
}
/// <summary>
/// Compares two values for equality
/// </summary>
public static bool operator ==(GeoEntry x, GeoEntry y)
{
return x.Position == y.Position && x.Member == y.Member;
}
/// <summary>
/// Compares two values for non-equality
/// </summary>
public static bool operator !=(GeoEntry x, GeoEntry y)
{
return x.Position != y.Position || x.Member != y.Member;
}
}
}
\ No newline at end of file
using System;
using System.ComponentModel;
namespace StackExchange.Redis
{
/// <summary>
/// Units associated with Geo Commands
/// </summary>
public enum GeoUnit
{
/// <summary>
/// Meters
/// </summary>
Meters,
/// <summary>
/// Kilometers
/// </summary>
Kilometers,
/// <summary>
/// Miles
/// </summary>
Miles,
/// <summary>
/// Feet
/// </summary>
Feet
}
}
\ No newline at end of file
......@@ -44,6 +44,86 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <remarks>http://redis.io/commands/debug-object</remarks>
RedisValue DebugObject(RedisKey key, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Add the specified member to the set stored at key. Specified members that are already a member of this set are ignored. If key does not exist, a new set is created before adding the specified members.
/// </summary>
/// <returns>True if the specified member was not already present in the set, else False</returns>
/// <remarks>http://redis.io/commands/geoadd</remarks>
bool GeoAdd(RedisKey key, double longitude, double latitude, RedisValue member, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Add the specified member to the set stored at key. Specified members that are already a member of this set are ignored. If key does not exist, a new set is created before adding the specified members.
/// </summary>
/// <returns>True if the specified member was not already present in the set, else False</returns>
/// <remarks>http://redis.io/commands/geoadd</remarks>
bool GeoAdd(RedisKey key, StackExchange.Redis.GeoEntry value, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Add the specified members to the set stored at key. Specified members that are already a member of this set are ignored. If key does not exist, a new set is created before adding the specified members.
/// </summary>
/// <returns>the number of elements that were added to the set, not including all the elements already present into the set.</returns>
/// <remarks>http://redis.io/commands/geoadd</remarks>
long GeoAdd(RedisKey key, GeoEntry[] values, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Removes the specified member from the geo sorted set stored at key. Non existing members are ignored.
/// </summary>
/// <returns>True if the member existed in the sorted set and was removed; False otherwise.</returns>
/// <remarks>http://redis.io/commands/zrem</remarks>
bool GeoRemove(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Return the distance between two members in the geospatial index represented by the sorted set.
/// </summary>
/// <returns>The command returns the distance as a double (represented as a string) in the specified unit, or NULL if one or both the elements are missing.</returns>
/// <remarks>http://redis.io/commands/geodist</remarks>
double? GeoDistance(RedisKey key, RedisValue member1, RedisValue member2, GeoUnit unit = GeoUnit.Meters, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Return valid Geohash strings representing the position of one or more elements in a sorted set value representing a geospatial index (where elements were added using GEOADD).
/// </summary>
/// <returns>The command returns an array where each element is the Geohash corresponding to each member name passed as argument to the command.</returns>
/// <remarks>http://redis.io/commands/geohash</remarks>
string[] GeoHash(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Return valid Geohash strings representing the position of one or more elements in a sorted set value representing a geospatial index (where elements were added using GEOADD).
/// </summary>
/// <returns>The command returns an array where each element is the Geohash corresponding to each member name passed as argument to the command.</returns>
/// <remarks>http://redis.io/commands/geohash</remarks>
string GeoHash(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Return the positions (longitude,latitude) of all the specified members of the geospatial index represented by the sorted set at key.
/// </summary>
/// <returns>The command returns an array where each element is a two elements array representing longitude and latitude (x,y) of each member name passed as argument to the command.Non existing elements are reported as NULL elements of the array.</returns>
/// <remarks>http://redis.io/commands/geopos</remarks>
GeoPosition?[] GeoPosition(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Return the positions (longitude,latitude) of all the specified members of the geospatial index represented by the sorted set at key.
/// </summary>
/// <returns>The command returns an array where each element is a two elements array representing longitude and latitude (x,y) of each member name passed as argument to the command.Non existing elements are reported as NULL elements of the array.</returns>
/// <remarks>http://redis.io/commands/geopos</remarks>
GeoPosition? GeoPosition(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Return the members of a sorted set populated with geospatial information using GEOADD, which are within the borders of the area specified with the center location and the maximum distance from the center (the radius).
/// </summary>
/// <returns>GeoRadiusResult[]</returns>
/// <remarks>http://redis.io/commands/georadius</remarks>
GeoRadiusResult[] GeoRadius(RedisKey key, RedisValue member, double radius, GeoUnit unit = GeoUnit.Meters, int count = -1, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Return the members of a sorted set populated with geospatial information using GEOADD, which are within the borders of the area specified with the center location and the maximum distance from the center (the radius).
/// </summary>
/// <returns>GeoRadiusResult[]</returns>
/// <remarks>http://redis.io/commands/georadius</remarks>
GeoRadiusResult[] GeoRadius(RedisKey key, double longitude, double latitude, double radius, GeoUnit unit = GeoUnit.Meters, int count = -1, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Decrements the number stored at field in the hash stored at key by decrement. If key does not exist, a new key holding a hash is created. If field does not exist or holds a string that cannot be interpreted as integer, the value is set to 0 before the operation is performed.
/// </summary>
......
......@@ -16,6 +16,87 @@ public interface IDatabaseAsync : IRedisAsync
/// <remarks>http://redis.io/commands/debug-object</remarks>
Task<RedisValue> DebugObjectAsync(RedisKey key, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Add the specified member to the set stored at key. Specified members that are already a member of this set are ignored. If key does not exist, a new set is created before adding the specified members.
/// </summary>
/// <returns>True if the specified member was not already present in the set, else False</returns>
/// <remarks>http://redis.io/commands/geoadd</remarks>
Task<bool> GeoAddAsync(RedisKey key, double longitude, double latitude, RedisValue member, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Add the specified member to the set stored at key. Specified members that are already a member of this set are ignored. If key does not exist, a new set is created before adding the specified members.
/// </summary>
/// <returns>True if the specified member was not already present in the set, else False</returns>
/// <remarks>http://redis.io/commands/geoadd</remarks>
Task<bool> GeoAddAsync(RedisKey key, StackExchange.Redis.GeoEntry value, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Add the specified members to the set stored at key. Specified members that are already a member of this set are ignored. If key does not exist, a new set is created before adding the specified members.
/// </summary>
/// <returns>the number of elements that were added to the set, not including all the elements already present into the set.</returns>
/// <remarks>http://redis.io/commands/geoadd</remarks>
Task<long> GeoAddAsync(RedisKey key, GeoEntry[] values, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Removes the specified member from the geo sorted set stored at key. Non existing members are ignored.
/// </summary>
/// <returns>True if the member existed in the sorted set and was removed; False otherwise.</returns>
/// <remarks>http://redis.io/commands/zrem</remarks>
Task<bool> GeoRemoveAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Return the distance between two members in the geospatial index represented by the sorted set.
/// </summary>
/// <returns>The command returns the distance as a double (represented as a string) in the specified unit, or NULL if one or both the elements are missing.</returns>
/// <remarks>http://redis.io/commands/geodist</remarks>
Task<double?> GeoDistanceAsync(RedisKey key, RedisValue member1, RedisValue member2, GeoUnit unit = GeoUnit.Meters, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Return valid Geohash strings representing the position of one or more elements in a sorted set value representing a geospatial index (where elements were added using GEOADD).
/// </summary>
/// <returns>The command returns an array where each element is the Geohash corresponding to each member name passed as argument to the command.</returns>
/// <remarks>http://redis.io/commands/geohash</remarks>
Task<string[]> GeoHashAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Return valid Geohash strings representing the position of one or more elements in a sorted set value representing a geospatial index (where elements were added using GEOADD).
/// </summary>
/// <returns>The command returns an array where each element is the Geohash corresponding to each member name passed as argument to the command.</returns>
/// <remarks>http://redis.io/commands/geohash</remarks>
Task<string> GeoHashAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Return the positions (longitude,latitude) of all the specified members of the geospatial index represented by the sorted set at key.
/// </summary>
/// <returns>The command returns an array where each element is a two elements array representing longitude and latitude (x,y) of each member name passed as argument to the command.Non existing elements are reported as NULL elements of the array.</returns>
/// <remarks>http://redis.io/commands/geopos</remarks>
Task<GeoPosition?[]> GeoPositionAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Return the positions (longitude,latitude) of all the specified members of the geospatial index represented by the sorted set at key.
/// </summary>
/// <returns>The command returns an array where each element is a two elements array representing longitude and latitude (x,y) of each member name passed as argument to the command.Non existing elements are reported as NULL elements of the array.</returns>
/// <remarks>http://redis.io/commands/geopos</remarks>
Task<GeoPosition?> GeoPositionAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Return the members of a sorted set populated with geospatial information using GEOADD, which are within the borders of the area specified with the center location and the maximum distance from the center (the radius).
/// </summary>
/// <returns>GeoRadiusResult[]</returns>
/// <remarks>http://redis.io/commands/georadius</remarks>
Task<GeoRadiusResult[]> GeoRadiusAsync(RedisKey key, RedisValue member, double radius, GeoUnit unit = GeoUnit.Meters, int count = -1, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Return the members of a sorted set populated with geospatial information using GEOADD, which are within the borders of the area specified with the center location and the maximum distance from the center (the radius).
/// </summary>
/// <returns>GeoRadiusResult[]</returns>
/// <remarks>http://redis.io/commands/georadius</remarks>
Task<GeoRadiusResult[]> GeoRadiusAsync(RedisKey key, double longitude, double latitude, double radius, GeoUnit unit = GeoUnit.Meters, int count = -1, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None);
/// <summary>
/// Increments the number stored at field in the hash stored at key by increment. If key does not exist, a new key holding a hash is created. If field does not exist or holds a string that cannot be interpreted as integer, the value is set to 0 before the operation is performed.
/// </summary>
......
......@@ -27,6 +27,59 @@ public RedisValue DebugObject(RedisKey key, CommandFlags flags = CommandFlags.No
return Inner.DebugObject(ToInner(key), flags);
}
public bool GeoAdd(RedisKey key, double longitude, double latitude, RedisValue member, CommandFlags flags = CommandFlags.None)
{
return Inner.GeoAdd(ToInner(key), longitude, latitude, member, flags);
}
public long GeoAdd(RedisKey key, GeoEntry[] geoEntries, CommandFlags flags = CommandFlags.None)
{
return Inner.GeoAdd(ToInner(key), geoEntries, flags);
}
public bool GeoAdd(RedisKey key, GeoEntry geoEntry, CommandFlags flags = CommandFlags.None)
{
return Inner.GeoAdd(ToInner(key), geoEntry, flags);
}
public bool GeoRemove(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
{
return Inner.GeoRemove(ToInner(key), member, flags);
}
public double? GeoDistance(RedisKey key, RedisValue value0, RedisValue value1, GeoUnit unit = GeoUnit.Meters,CommandFlags flags = CommandFlags.None)
{
return Inner.GeoDistance(ToInner(key), value0, value1, unit, flags);
}
public string[] GeoHash(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
{
return Inner.GeoHash(ToInner(key), members, flags);
}
public string GeoHash(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
{
return Inner.GeoHash(ToInner(key), member, flags);
}
public GeoPosition?[] GeoPosition(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
{
return Inner.GeoPosition(ToInner(key), members, flags);
}
public GeoPosition? GeoPosition(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
{
return Inner.GeoPosition(ToInner(key), member, flags);
}
public GeoRadiusResult[] GeoRadius(RedisKey key, RedisValue member, double radius, GeoUnit unit = GeoUnit.Meters, int count = -1, Order? order = null,GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None)
{
return Inner.GeoRadius(ToInner(key), member, radius, unit, count, order, options, flags);
}
public GeoRadiusResult[] GeoRadius(RedisKey key, double longitude, double latitude, double radius, GeoUnit unit = GeoUnit.Meters, int count = -1, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None)
{
return Inner.GeoRadius(ToInner(key), longitude, latitude, radius, unit, count, order, options, flags);
}
public double HashDecrement(RedisKey key, RedisValue hashField, double value, CommandFlags flags = CommandFlags.None)
{
return Inner.HashDecrement(ToInner(key), hashField, value, flags);
......
......@@ -24,6 +24,41 @@ public Task<RedisValue> DebugObjectAsync(RedisKey key, CommandFlags flags = Comm
return Inner.DebugObjectAsync(ToInner(key), flags);
}
public Task<bool> GeoAddAsync(RedisKey key, double longitude, double latitude, RedisValue member, CommandFlags flags = CommandFlags.None)
=> Inner.GeoAddAsync(ToInner(key), longitude, latitude, member, flags);
public Task<bool> GeoAddAsync(RedisKey key, StackExchange.Redis.GeoEntry value, CommandFlags flags = CommandFlags.None)
=> Inner.GeoAddAsync(ToInner(key), value, flags);
public Task<long> GeoAddAsync(RedisKey key, StackExchange.Redis.GeoEntry[] values, CommandFlags flags = CommandFlags.None)
=> Inner.GeoAddAsync(ToInner(key), values, flags);
public Task<bool> GeoRemoveAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
=> Inner.GeoRemoveAsync(ToInner(key), member, flags);
public Task<double?> GeoDistanceAsync(RedisKey key, RedisValue member1, RedisValue member2, GeoUnit unit = GeoUnit.Meters, CommandFlags flags = CommandFlags.None)
=> Inner.GeoDistanceAsync(ToInner(key), member1, member2, unit, flags);
public Task<string[]> GeoHashAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
=> Inner.GeoHashAsync(ToInner(key), members, flags);
public Task<string> GeoHashAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
=> Inner.GeoHashAsync(ToInner(key), member, flags);
public Task<GeoPosition?[]> GeoPositionAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
=> Inner.GeoPositionAsync(ToInner(key), members, flags);
public Task<GeoPosition?> GeoPositionAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
=> Inner.GeoPositionAsync(ToInner(key), member, flags);
public Task<GeoRadiusResult[]> GeoRadiusAsync(RedisKey key, RedisValue member, double radius, GeoUnit unit = GeoUnit.Meters, int count = -1, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None)
=> Inner.GeoRadiusAsync(ToInner(key), member, radius, unit, count, order, options, flags);
public Task<GeoRadiusResult[]> GeoRadiusAsync(RedisKey key, double longitude, double latitude, double radius, GeoUnit unit = GeoUnit.Meters, int count = -1, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None)
=> Inner.GeoRadiusAsync(ToInner(key), longitude, latitude, radius, unit, count, order, options, flags);
public Task<double> HashDecrementAsync(RedisKey key, RedisValue hashField, double value, CommandFlags flags = CommandFlags.None)
{
return Inner.HashDecrementAsync(ToInner(key), hashField, value, flags);
......
......@@ -350,6 +350,29 @@ public static Message Create(int db, CommandFlags flags, RedisCommand command, R
return new CommandKeyValueValueValueMessage(db, flags, command, key, value0, value1, value2);
}
public static Message Create(int db, CommandFlags flags, RedisCommand command, RedisKey key, GeoEntry[] values)
{
if (values == null) throw new ArgumentNullException(nameof(values));
if (values.Length == 0)
{
throw new ArgumentOutOfRangeException(nameof(values));
}
if (values.Length == 1)
{
var value = values[0];
return Message.Create(db, flags, command, key, value.Longitude, value.Latitude, value.Member);
}
var arr = new RedisValue[3 * values.Length];
int index = 0;
foreach (var value in values)
{
arr[index++] = value.Longitude;
arr[index++] = value.Latitude;
arr[index++] = value.Member;
}
return new CommandKeyValuesMessage(db, flags, command, key, arr);
}
public static Message Create(int db, CommandFlags flags, RedisCommand command, RedisKey key, RedisValue value0, RedisValue value1, RedisValue value2, RedisValue value3)
{
return new CommandKeyValueValueValueValueMessage(db, flags, command, key, value0, value1, value2, value3);
......@@ -907,7 +930,7 @@ public CommandKeysMessage(int db, CommandFlags flags, RedisCommand command, Redi
public override int GetHashSlot(ServerSelectionStrategy serverSelectionStrategy)
{
int slot = ServerSelectionStrategy.NoSlot;
for(int i = 0; i < keys.Length; i++)
for (int i = 0; i < keys.Length; i++)
{
slot = serverSelectionStrategy.CombineSlot(slot, keys[i]);
}
......
......@@ -230,6 +230,78 @@ internal RedisValue[] GetItemsAsValues()
return arr;
}
}
static readonly string[] NilStrings = new string[0];
internal string[] GetItemsAsStrings()
{
RawResult[] items = GetItems();
if (items == null)
{
return null;
}
else if (items.Length == 0)
{
return NilStrings;
}
else
{
var arr = new string[items.Length];
for (int i = 0; i < arr.Length; i++)
{
arr[i] = (string)(items[i].AsRedisValue());
}
return arr;
}
}
internal GeoPosition? GetItemsAsGeoPosition()
{
RawResult[] items = GetItems();
if (items == null || items.Length == 0)
{
return null;
}
var coords = items[0].GetArrayOfRawResults();
if (coords == null)
{
return null;
}
return new GeoPosition((double)coords[0].AsRedisValue(), (double)coords[1].AsRedisValue());
}
internal GeoPosition?[] GetItemsAsGeoPositionArray()
{
RawResult[] items = GetItems();
if (items == null)
{
return null;
}
else if (items.Length == 0)
{
return new GeoPosition?[0];
}
else
{
var arr = new GeoPosition?[items.Length];
for (int i = 0; i < arr.Length; i++)
{
RawResult[] item = items[i].GetArrayOfRawResults();
if (item == null)
{
arr[i] = null;
}
else
{
arr[i] = new GeoPosition((double)item[0].AsRedisValue(), (double)item[1].AsRedisValue());
}
}
return arr;
}
}
internal RawResult[] GetItemsAsRawResults()
{
return GetItems();
}
// returns an array of RawResults
internal RawResult[] GetArrayOfRawResults()
......
......@@ -38,6 +38,13 @@ enum RedisCommand
FLUSHALL,
FLUSHDB,
GEOADD,
GEODIST,
GEOHASH,
GEOPOS,
GEORADIUS,
GEORADIUSBYMEMBER,
GET,
GETBIT,
GETRANGE,
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace StackExchange.Redis
......@@ -44,13 +46,179 @@ public RedisValue DebugObject(RedisKey key, CommandFlags flags = CommandFlags.No
var msg = Message.Create(Database, flags, RedisCommand.DEBUG, RedisLiterals.OBJECT, key);
return ExecuteSync(msg, ResultProcessor.RedisValue);
}
public Task<RedisValue> DebugObjectAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.DEBUG, RedisLiterals.OBJECT, key);
return ExecuteAsync(msg, ResultProcessor.RedisValue);
}
public bool GeoAdd(RedisKey key, double longitude, double latitude, RedisValue member, CommandFlags flags = CommandFlags.None)
{
return GeoAdd(key, new GeoEntry(longitude, latitude, member), flags);
}
public Task<bool> GeoAddAsync(RedisKey key, double longitude, double latitude, RedisValue member, CommandFlags flags = CommandFlags.None)
{
return GeoAddAsync(key, new GeoEntry(longitude, latitude, member), flags);
}
public bool GeoAdd(RedisKey key, GeoEntry value, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.GEOADD, key, value.Longitude, value.Latitude, value.Member);
return ExecuteSync(msg, ResultProcessor.Boolean);
}
public Task<bool> GeoAddAsync(RedisKey key, GeoEntry value, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.GEOADD, key, value.Longitude, value.Latitude, value.Member);
return ExecuteAsync(msg, ResultProcessor.Boolean);
}
public long GeoAdd(RedisKey key, GeoEntry[] values, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.GEOADD, key, values);
return ExecuteSync(msg, ResultProcessor.Int64);
}
public Task<long> GeoAddAsync(RedisKey key, GeoEntry[] values, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.GEOADD, key, values);
return ExecuteAsync(msg, ResultProcessor.Int64);
}
public bool GeoRemove(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
{
return SortedSetRemove(key, member, flags);
}
public Task<bool> GeoRemoveAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
{
return SortedSetRemoveAsync(key, member, flags);
}
public double? GeoDistance(RedisKey key, RedisValue value0, RedisValue value1, GeoUnit unit = GeoUnit.Meters, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.GEODIST, key, value0, value1, StackExchange.Redis.GeoPosition.GetRedisUnit(unit));
return ExecuteSync(msg, ResultProcessor.NullableDouble);
}
public Task<double?> GeoDistanceAsync(RedisKey key, RedisValue value0, RedisValue value1, GeoUnit unit = GeoUnit.Meters, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.GEODIST, key, value0, value1, StackExchange.Redis.GeoPosition.GetRedisUnit(unit));
return ExecuteAsync(msg, ResultProcessor.NullableDouble);
}
public string[] GeoHash(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
{
if (members == null) throw new ArgumentNullException(nameof(members));
var redisValues = new RedisValue[members.Length];
for (var i = 0; i < members.Length; i++) redisValues[i] = members[i];
var msg = Message.Create(Database, flags, RedisCommand.GEOHASH, key, redisValues);
return ExecuteSync(msg, ResultProcessor.StringArray);
}
public Task<string[]> GeoHashAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
{
if (members == null) throw new ArgumentNullException(nameof(members));
var redisValues = new RedisValue[members.Length];
for (var i = 0; i < members.Length; i++) redisValues[i] = members[i];
var msg = Message.Create(Database, flags, RedisCommand.GEOHASH, key, redisValues);
return ExecuteAsync(msg, ResultProcessor.StringArray);
}
public string GeoHash(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.GEOHASH, key, member);
return ExecuteSync(msg, ResultProcessor.String);
}
public Task<string> GeoHashAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.GEOHASH, key, member);
return ExecuteAsync(msg, ResultProcessor.String);
}
public GeoPosition?[] GeoPosition(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
{
if (members == null) throw new ArgumentNullException(nameof(members));
var redisValues = new RedisValue[members.Length];
for (var i = 0; i < members.Length; i++) redisValues[i] = members[i];
var msg = Message.Create(Database, flags, RedisCommand.GEOPOS, key, redisValues);
return ExecuteSync(msg, ResultProcessor.RedisGeoPositionArray);
}
public Task<GeoPosition?[]> GeoPositionAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
{
if (members == null) throw new ArgumentNullException(nameof(members));
var redisValues = new RedisValue[members.Length];
for (var i = 0; i < members.Length; i++) redisValues[i] = members[i];
var msg = Message.Create(Database, flags, RedisCommand.GEOPOS, key, redisValues);
return ExecuteAsync(msg, ResultProcessor.RedisGeoPositionArray);
}
public GeoPosition? GeoPosition(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.GEOPOS, key, member);
return ExecuteSync(msg, ResultProcessor.RedisGeoPosition);
}
public Task<GeoPosition?> GeoPositionAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.GEOPOS, key, member);
return ExecuteAsync(msg, ResultProcessor.RedisGeoPosition);
}
static readonly RedisValue
WITHCOORD = Encoding.ASCII.GetBytes("WITHCOORD"),
WITHDIST = Encoding.ASCII.GetBytes("WITHDIST"),
WITHHASH = Encoding.ASCII.GetBytes("WITHHASH"),
COUNT = Encoding.ASCII.GetBytes("COUNT"),
ASC = Encoding.ASCII.GetBytes("ASC"),
DESC = Encoding.ASCII.GetBytes("DESC");
private Message GetGeoRadiusMessage(RedisKey key, RedisValue? member, double longitude, double latitude, double radius, GeoUnit unit, int count, Order? order, GeoRadiusOptions options, CommandFlags flags)
{
var redisValues = new List<RedisValue>();
RedisCommand command;
if (member == null)
{
redisValues.Add(longitude);
redisValues.Add(latitude);
command = RedisCommand.GEORADIUS;
}
else
{
redisValues.Add(member.Value);
command = RedisCommand.GEORADIUSBYMEMBER;
}
redisValues.Add(radius);
redisValues.Add(StackExchange.Redis.GeoPosition.GetRedisUnit(unit));
if ((options & GeoRadiusOptions.WithCoordinates) != 0) redisValues.Add(WITHCOORD);
if ((options & GeoRadiusOptions.WithDistance) != 0) redisValues.Add(WITHDIST);
if ((options & GeoRadiusOptions.WithGeoHash) != 0) redisValues.Add(WITHHASH);
if (count > 0)
{
redisValues.Add(COUNT);
redisValues.Add(count);
}
if (order != null)
{
switch (order.Value)
{
case Order.Ascending: redisValues.Add(ASC); break;
case Order.Descending: redisValues.Add(DESC); break;
default: throw new ArgumentOutOfRangeException(nameof(order));
}
}
return Message.Create(Database, flags, command, key, redisValues.ToArray());
}
public GeoRadiusResult[] GeoRadius(RedisKey key, RedisValue member, double radius, GeoUnit unit, int count, Order? order, GeoRadiusOptions options, CommandFlags flags)
{
return ExecuteSync(GetGeoRadiusMessage(key, member, double.NaN, double.NaN, radius, unit, count, order, options, flags), ResultProcessor.GeoRadiusArray(options));
}
public Task<GeoRadiusResult[]> GeoRadiusAsync(RedisKey key, RedisValue member, double radius, GeoUnit unit, int count, Order? order, GeoRadiusOptions options, CommandFlags flags)
{
return ExecuteAsync(GetGeoRadiusMessage(key, member, double.NaN, double.NaN, radius, unit, count, order, options, flags), ResultProcessor.GeoRadiusArray(options));
}
public GeoRadiusResult[] GeoRadius(RedisKey key, double longitude, double latitude, double radius, GeoUnit unit, int count, Order? order, GeoRadiusOptions options, CommandFlags flags)
{
return ExecuteSync(GetGeoRadiusMessage(key, null, longitude, latitude, radius, unit, count, order, options, flags), ResultProcessor.GeoRadiusArray(options));
}
public Task<GeoRadiusResult[]> GeoRadiusAsync(RedisKey key, double longitude, double latitude, double radius, GeoUnit unit, int count, Order? order, GeoRadiusOptions options, CommandFlags flags)
{
return ExecuteAsync(GetGeoRadiusMessage(key, null, longitude, latitude, radius, unit, count, order, options, flags), ResultProcessor.GeoRadiusArray(options));
}
public long HashDecrement(RedisKey key, RedisValue hashField, long value = 1, CommandFlags flags = CommandFlags.None)
{
return HashIncrement(key, hashField, -value, flags);
......@@ -926,7 +1094,6 @@ public Task<RedisResult> ScriptEvaluateAsync(LoadedLuaScript script, object para
{
return script.EvaluateAsync(this, parameters, null, flags);
}
public bool SetAdd(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.SADD, key, value);
......@@ -1685,7 +1852,8 @@ private Message GetHashSetMessage(RedisKey key, HashEntry[] hashFields, CommandF
switch (hashFields.Length)
{
case 0: return null;
case 1: return Message.Create(Database, flags, RedisCommand.HMSET, key,
case 1:
return Message.Create(Database, flags, RedisCommand.HMSET, key,
hashFields[0].name, hashFields[0].value);
case 2:
return Message.Create(Database, flags, RedisCommand.HMSET, key,
......
......@@ -27,7 +27,8 @@ public struct RedisFeatures
v2_8_12 = new Version(2, 8, 12),
v2_8_18 = new Version(2, 8, 18),
v2_9_5 = new Version(2, 9, 5),
v3_0_0 = new Version(3, 0, 0);
v3_0_0 = new Version(3, 0, 0),
v3_2_0 = new Version(3, 2, 0);
private readonly Version version;
/// <summary>
......@@ -144,6 +145,11 @@ public RedisFeatures(Version version)
/// </summary>
public bool HyperLogLogCountSlaveSafe => Version >= v2_8_18;
/// <summary>
/// Are the GEO commands available?
/// </summary>
public bool Geo => Version >= v3_2_0;
/// <summary>
/// The Redis version of the server
/// </summary>
......
......@@ -66,6 +66,15 @@ abstract class ResultProcessor
public static readonly ResultProcessor<RedisValue[]>
RedisValueArray = new RedisValueArrayProcessor();
public static readonly ResultProcessor<string[]>
StringArray = new StringArrayProcessor();
public static readonly ResultProcessor<GeoPosition?[]>
RedisGeoPositionArray = new RedisValueGeoPositionArrayProcessor();
public static readonly ResultProcessor<GeoPosition?>
RedisGeoPosition = new RedisValueGeoPositionProcessor();
public static readonly ResultProcessor<TimeSpan>
ResponseTimer = new TimingProcessor();
......@@ -75,6 +84,8 @@ abstract class ResultProcessor
public static readonly SortedSetEntryArrayProcessor
SortedSetWithScores = new SortedSetEntryArrayProcessor();
public static ResultProcessor<GeoRadiusResult[]> GeoRadiusArray(GeoRadiusOptions options) => GeoRadiusResultArrayProcessor.Get(options);
public static readonly ResultProcessor<string>
String = new StringProcessor(),
ClusterNodesRaw = new ClusterNodesRawProcessor();
......@@ -204,7 +215,7 @@ public virtual bool SetResult(PhysicalConnection connection, Message message, Ra
private void UnexpectedResponse(Message message, RawResult result)
{
ConnectionMultiplexer.TraceWithoutContext("From " + GetType().Name, "Unexpected Response");
ConnectionFail(message, ConnectionFailureType.ProtocolFailure, "Unexpected response to " + (message?.Command.ToString() ?? "n/a") +": " + result.ToString());
ConnectionFail(message, ConnectionFailureType.ProtocolFailure, "Unexpected response to " + (message?.Command.ToString() ?? "n/a") + ": " + result.ToString());
}
public sealed class TimeSpanProcessor : ResultProcessor<TimeSpan?>
......@@ -314,7 +325,7 @@ public sealed class TrackSubscriptionsProcessor : ResultProcessor<bool>
{
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result)
{
if(result.Type == ResultType.MultiBulk)
if (result.Type == ResultType.MultiBulk)
{
var items = result.GetItems();
long count;
......@@ -503,7 +514,7 @@ sealed class AutoConfigureProcessor : ResultProcessor<bool>
static readonly byte[] READONLY = Encoding.UTF8.GetBytes("READONLY ");
public override bool SetResult(PhysicalConnection connection, Message message, RawResult result)
{
if(result.IsError && result.AssertStarts(READONLY))
if (result.IsError && result.AssertStarts(READONLY))
{
var server = connection.Bridge.ServerEndPoint;
server.Multiplexer.Trace("Auto-configured role: slave");
......@@ -584,7 +595,7 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
break;
}
}
else if((val = Extract(line, "run_id:")) != null)
else if ((val = Extract(line, "run_id:")) != null)
{
server.RunId = val;
}
......@@ -927,11 +938,11 @@ class PubSubNumSubProcessor : Int64Processor
{
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result)
{
if(result.Type == ResultType.MultiBulk)
if (result.Type == ResultType.MultiBulk)
{
var arr = result.GetItems();
long val;
if(arr != null && arr.Length == 2 && arr[1].TryGetInt64(out val))
if (arr != null && arr.Length == 2 && arr[1].TryGetInt64(out val))
{
SetResult(message, val);
return true;
......@@ -950,7 +961,7 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
case ResultType.Integer:
case ResultType.SimpleString:
case ResultType.BulkString:
if(result.IsNull)
if (result.IsNull)
{
SetResult(message, null);
return true;
......@@ -975,7 +986,7 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
case ResultType.Integer:
case ResultType.SimpleString:
case ResultType.BulkString:
if(result.IsNull)
if (result.IsNull)
{
SetResult(message, null);
return true;
......@@ -1091,11 +1102,140 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
return false;
}
}
sealed class StringArrayProcessor : ResultProcessor<string[]>
{
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result)
{
switch (result.Type)
{
case ResultType.MultiBulk:
var arr = result.GetItemsAsStrings();
SetResult(message, arr);
return true;
}
return false;
}
}
sealed class RedisValueGeoPositionProcessor : ResultProcessor<GeoPosition?>
{
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result)
{
switch (result.Type)
{
case ResultType.MultiBulk:
var pos = result.GetItemsAsGeoPosition();
SetResult(message, pos);
return true;
}
return false;
}
}
sealed class RedisValueGeoPositionArrayProcessor : ResultProcessor<GeoPosition?[]>
{
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result)
{
switch (result.Type)
{
case ResultType.MultiBulk:
var arr = result.GetItemsAsGeoPositionArray();
SetResult(message, arr);
return true;
}
return false;
}
}
sealed class GeoRadiusResultArrayProcessor : ResultProcessor<GeoRadiusResult[]>
{
private static readonly GeoRadiusResultArrayProcessor[] instances;
private readonly GeoRadiusOptions options;
static GeoRadiusResultArrayProcessor()
{
instances = new GeoRadiusResultArrayProcessor[8];
for (int i = 0; i < 8; i++) instances[i] = new GeoRadiusResultArrayProcessor((GeoRadiusOptions)i);
}
public static GeoRadiusResultArrayProcessor Get(GeoRadiusOptions options)
{
int i = (int)options;
if (i < 0 || i >= instances.Length) throw new ArgumentOutOfRangeException(nameof(options));
return instances[i];
}
private GeoRadiusResultArrayProcessor(GeoRadiusOptions options)
{
this.options = options;
}
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result)
{
switch (result.Type)
{
case ResultType.MultiBulk:
var arr = result.GetItemsAsRawResults();
GeoRadiusResult[] typed;
if (arr == null)
{
typed = null;
}
else
{
var options = this.options;
typed = new GeoRadiusResult[arr.Length];
for (int i = 0; i < arr.Length; i++)
{
typed[i] = Parse(options, arr[i]);
}
}
SetResult(message, typed);
return true;
}
return false;
}
private static GeoRadiusResult Parse(GeoRadiusOptions options, RawResult item)
{
if (options == GeoRadiusOptions.None)
{
// Without any WITH option specified, the command just returns a linear array like ["New York","Milan","Paris"].
return new GeoRadiusResult(item.AsRedisValue(), null, null, null);
}
// If WITHCOORD, WITHDIST or WITHHASH options are specified, the command returns an array of arrays, where each sub-array represents a single item.
var arr = item.GetArrayOfRawResults();
int index = 0;
// the first item in the sub-array is always the name of the returned item.
var member = arr[index++].AsRedisValue();
/* The other information is returned in the following order as successive elements of the sub-array.
The distance from the center as a floating point number, in the same unit specified in the radius.
The geohash integer.
The coordinates as a two items x,y array (longitude,latitude).
*/
double? distance = null;
GeoPosition? position = null;
long? hash = null;
if ((options & GeoRadiusOptions.WithDistance) != 0) { distance = (double?)arr[index++].AsRedisValue(); }
if ((options & GeoRadiusOptions.WithGeoHash) != 0) { hash = (long?)arr[index++].AsRedisValue(); }
if ((options & GeoRadiusOptions.WithCoordinates) != 0)
{
var coords = arr[index++].GetArrayOfRawResults();
double longitude = (double)coords[0].AsRedisValue(), latitude = (double)coords[1].AsRedisValue();
position = new GeoPosition(longitude, latitude);
}
return new GeoRadiusResult(member, distance, hash, position);
}
}
sealed class RedisValueProcessor : ResultProcessor<RedisValue>
{
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result)
{
switch(result.Type)
switch (result.Type)
{
case ResultType.Integer:
case ResultType.SimpleString:
......@@ -1153,6 +1293,14 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
case ResultType.BulkString:
SetResult(message, result.GetString());
return true;
case ResultType.MultiBulk:
var arr = result.GetItems();
if(arr.Length == 1)
{
SetResult(message, arr[0].GetString());
return true;
}
break;
}
return false;
}
......@@ -1194,7 +1342,7 @@ public override bool SetResult(PhysicalConnection connection, Message message, R
protected override bool SetResultCore(PhysicalConnection connection, Message message, RawResult result)
{
bool happy;
switch(message.Command)
switch (message.Command)
{
case RedisCommand.ECHO:
happy = result.Type == ResultType.BulkString && (!establishConnection || result.IsEqual(connection.Multiplexer.UniqueId));
......@@ -1212,9 +1360,9 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
happy = true;
break;
}
if(happy)
if (happy)
{
if(establishConnection) connection.Bridge.OnFullyEstablished(connection);
if (establishConnection) connection.Bridge.OnFullyEstablished(connection);
SetResult(message, happy);
return true;
}
......
......@@ -4,6 +4,10 @@
namespace StackExchange.Redis
{
/// <summary>
/// Describes a sorted-set element with the corresponding value
/// </summary>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment