Commit b2622101 authored by Marc Gravell's avatar Marc Gravell

GEO: tests for all geo methods working against Azure

parent 4ef40bf2
using System.Linq;
using System.Runtime.Remoting;
using NUnit.Framework;
using System;
using System.IO;
using System.Linq;
namespace StackExchange.Redis.Tests
{
public class AzureTestAttribute : TestAttribute
{
}
[TestFixture]
public class GeoTests : TestBase
{
[Test]
public void GeoAddEveryWay()
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(3);
var added1 = db.GeoAdd("Sicily", 14.361389, 39.115556, "PalermoPlusOne");
var geo1 = new GeoEntry(13.361389, 38.115556, "Palermo");
var geo2 = new GeoEntry(15.087269, 37.502669, "Catania");
var added2 = db.GeoAdd("Sicily",new GeoEntry[] {geo1,geo2});
Assert.IsTrue(added1 & (added2==2));
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));
}
}
[Test]
public void GetGeoDist()
[AzureTest]
public void GetDistance()
{
using (var conn = Create())
{
var db = conn.GetDatabase(3);
var geo1 = new GeoEntry(13.361389, 38.115556, "Palermo");
var geo2 = new GeoEntry(15.087269, 37.502669, "Catania");
var added2 = db.GeoAdd("Sicily", new GeoEntry[] { geo1, geo2 });
var val = db.GeoDistance("Sicily", "Palermo", "Catania",GeoUnit.Meters);
Assert.Equals(166274.15156960039, (double) val);
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);
}
}
[Test]
public void AddSetEveryWay()
[AzureTest]
public void GeoHash()
{
using (var conn = Create())
{
var db = conn.GetDatabase(3);
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.SetAdd(key, "a");
db.SetAdd(key, new RedisValue[] { "b" });
db.SetAdd(key, new RedisValue[] { "c", "d" });
db.SetAdd(key, new RedisValue[] { "e", "f", "g" });
db.SetAdd(key, new RedisValue[] { "h", "i", "j", "k" });
var vals = db.SetMembers(key);
string s = string.Join(",", vals.OrderByDescending(x => x));
Assert.AreEqual("k,j,i,h,g,f,e,d,c,b,a", s);
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);
}
}
[Test]
public void AddSetEveryWayNumbers()
[AzureTest]
public void GeoRemove()
{
using (var conn = Create())
{
var db = conn.GetDatabase(3);
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.SetAdd(key, "a");
db.SetAdd(key, new RedisValue[] { "1" });
db.SetAdd(key, new RedisValue[] { "11", "2" });
db.SetAdd(key, new RedisValue[] { "10", "3", "1.5" });
db.SetAdd(key, new RedisValue[] { "2.2", "-1", "s", "t" });
var vals = db.SetMembers(key);
string s = string.Join(",", vals.OrderByDescending(x => x));
Assert.AreEqual("t,s,a,11,10,3,2.2,2,1.5,1,-1", s);
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
{
......
......@@ -135,7 +135,7 @@ public void Teardown()
}
protected const int PrimaryPort = 6379, SlavePort = 6380, SecurePort = 6381;
protected const string PrimaryServer = "192.168.10.120", SecurePassword = "changeme", PrimaryPortString = "6379", SlavePortString = "6380", SecurePortString = "6381";
protected const string PrimaryServer = "127.0.0.1", SecurePassword = "changeme", PrimaryPortString = "6379", SlavePortString = "6380", SecurePortString = "6381";
internal static Task Swallow(Task task)
{
if (task != null) task.ContinueWith(swallowErrors, TaskContinuationOptions.OnlyOnFaulted);
......
......@@ -36,6 +36,10 @@ public enum GeoRadiusOptions
/// </summary>
public struct GeoRadiusResult
{
/// <summary>
/// Indicate the member being represented
/// </summary>
public override string ToString() => Member.ToString();
/// <summary>
/// The matched member.
/// </summary>
......@@ -169,7 +173,7 @@ public struct GeoEntry : IEquatable<GeoEntry>
/// <summary>
/// Describes the longitude and latitude of a GeoEntry
/// </summary>
public GeoPosition Point { get; }
public GeoPosition Position { get; }
/// <summary>
/// Initializes a GeoEntry value
......@@ -177,7 +181,7 @@ public struct GeoEntry : IEquatable<GeoEntry>
public GeoEntry(double longitude, double latitude, RedisValue member)
{
Member = member;
Point = new GeoPosition(longitude, latitude);
Position = new GeoPosition(longitude, latitude);
}
......@@ -185,12 +189,12 @@ public GeoEntry(double longitude, double latitude, RedisValue member)
/// <summary>
/// The longitude of the geo entry
/// </summary>
public double Longitude => Point.Longitude;
public double Longitude => Position.Longitude;
/// <summary>
/// The latitude of the geo entry
/// </summary>
public double Latitude => Point.Latitude;
public double Latitude => Position.Latitude;
/// <summary>
/// See Object.ToString()
......@@ -204,7 +208,7 @@ public override string ToString()
/// </summary>
public override int GetHashCode()
{
return Point.GetHashCode() ^ Member.GetHashCode();
return Position.GetHashCode() ^ Member.GetHashCode();
}
/// <summary>
/// Compares two values for equality
......@@ -225,14 +229,14 @@ public bool Equals(GeoEntry value)
/// </summary>
public static bool operator ==(GeoEntry x, GeoEntry y)
{
return x.Point == y.Point && x.Member == y.Member;
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.Point != y.Point || x.Member != y.Member;
return x.Position != y.Position || x.Member != y.Member;
}
}
}
\ No newline at end of file
......@@ -79,7 +79,7 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// </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);
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).
......
......@@ -51,7 +51,7 @@ public interface IDatabaseAsync : IRedisAsync
/// </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);
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).
......
......@@ -46,7 +46,7 @@ public bool GeoRemove(RedisKey key, RedisValue member, CommandFlags flags = Comm
return Inner.GeoRemove(ToInner(key), member, flags);
}
public double GeoDistance(RedisKey key, RedisValue value0, RedisValue value1, GeoUnit unit = GeoUnit.Meters,CommandFlags flags = CommandFlags.None)
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);
}
......
......@@ -36,7 +36,7 @@ public Task<long> GeoAddAsync(RedisKey key, StackExchange.Redis.GeoEntry[] value
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)
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)
......
......@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace StackExchange.Redis
......@@ -92,17 +93,16 @@ public Task<bool> GeoRemoveAsync(RedisKey key, RedisValue member, CommandFlags f
return SortedSetRemoveAsync(key, member, flags);
}
public double GeoDistance(RedisKey key, RedisValue value0, RedisValue value1, GeoUnit unit = GeoUnit.Meters, CommandFlags flags = CommandFlags.None)
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.Double);
return ExecuteSync(msg, ResultProcessor.NullableDouble);
}
public Task<double> GeoDistanceAsync(RedisKey key, RedisValue value0, RedisValue value1, GeoUnit unit = GeoUnit.Meters, CommandFlags flags = CommandFlags.None)
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.Double);
return ExecuteAsync(msg, ResultProcessor.NullableDouble);
}
public string[] GeoHash(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
{
if (members == null) throw new ArgumentNullException(nameof(members));
......@@ -158,6 +158,13 @@ public Task<string> GeoHashAsync(RedisKey key, RedisValue member, CommandFlags f
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>();
......@@ -175,16 +182,20 @@ private Message GetGeoRadiusMessage(RedisKey key, RedisValue? member, double lon
}
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);
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;
case Order.Ascending: redisValues.Add(ASC); break;
case Order.Descending: redisValues.Add(DESC); break;
default: throw new ArgumentOutOfRangeException(nameof(order));
}
}
......
......@@ -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>
......
......@@ -1293,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;
}
......
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