From 126513f1fb84749ec26e99a47369964fd7db8f74 Mon Sep 17 00:00:00 2001
From: Ian Chan <icha024@gmail.com>
Date: Wed, 14 Oct 2015 22:39:39 +0100
Subject: [PATCH] Added binding for GEORADIUSBYMEMBER, GEODIST AND GEOHASH.

Change-Id: Ia6144617f42629af4c022e595c444ddc6d66f1a3
---
 command.go       |  15 ++---
 commands.go      |  49 +++++++++++----
 commands_test.go | 161 ++++++++++++++++++++++++++++++++++++++---------
 3 files changed, 176 insertions(+), 49 deletions(-)

diff --git a/command.go b/command.go
index 850d1eb4..1fbadc08 100644
--- a/command.go
+++ b/command.go
@@ -779,16 +779,13 @@ type GeoLocation struct {
 
 // GeoRadiusQuery is used with GeoRadius to query geospatial index.
 type GeoRadiusQuery struct {
-	Key       string
-	Longitude float64
-	Latitude  float64
-	Radius    float64
+	Radius float64
 	// Can be m, km, ft, or mi. Default is km.
-	Unit            string
-	WithCoordinates bool
-	WithDistance    bool
-	WithGeoHash     bool
-	Count           int
+	Unit        string
+	WithCoord   bool
+	WithDist    bool
+	WithGeoHash bool
+	Count       int
 	// Can be ASC or DESC. Default is no sort order.
 	Sort string
 }
diff --git a/commands.go b/commands.go
index 6572c81e..6f9a7c7c 100644
--- a/commands.go
+++ b/commands.go
@@ -1676,22 +1676,17 @@ func (c *commandable) GeoAdd(key string, geoLocation ...*GeoLocation) *IntCmd {
 	return cmd
 }
 
-func (c *commandable) GeoRadius(query *GeoRadiusQuery) *GeoLocationCmd {
-	args := make([]interface{}, 6)
-	args[0] = "GEORADIUS"
-	args[1] = query.Key
-	args[2] = query.Longitude
-	args[3] = query.Latitude
-	args[4] = query.Radius
+func (c *commandable) geoRadius(args []interface{}, query *GeoRadiusQuery) *GeoLocationCmd {
+	args = append(args, query.Radius)
 	if query.Unit != "" {
-		args[5] = query.Unit
+		args = append(args, query.Unit)
 	} else {
-		args[5] = "km"
+		args = append(args, "km")
 	}
-	if query.WithCoordinates {
+	if query.WithCoord {
 		args = append(args, "WITHCOORD")
 	}
-	if query.WithDistance {
+	if query.WithDist {
 		args = append(args, "WITHDIST")
 	}
 	if query.WithGeoHash {
@@ -1703,8 +1698,38 @@ func (c *commandable) GeoRadius(query *GeoRadiusQuery) *GeoLocationCmd {
 	if query.Sort != "" {
 		args = append(args, query.Sort)
 	}
-
 	cmd := NewGeoLocationCmd(args...)
 	c.Process(cmd)
 	return cmd
 }
+
+func (c *commandable) GeoRadius(key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd {
+	args := []interface{}{"GEORADIUS", key, longitude, latitude}
+	return c.geoRadius(args, query)
+}
+
+func (c *commandable) GeoRadiusByMember(key, member string, query *GeoRadiusQuery) *GeoLocationCmd {
+	args := []interface{}{"GEORADIUSBYMEMBER", key, member}
+	return c.geoRadius(args, query)
+}
+
+func (c *commandable) GeoDist(key string, member1, member2, unit string) *FloatCmd {
+	if unit == "" {
+		unit = "km"
+	}
+	cmd := NewFloatCmd("GEODIST", key, member1, member2, unit)
+	c.Process(cmd)
+	return cmd
+}
+
+func (c *commandable) GeoHash(key string, members ...string) *StringSliceCmd {
+	args := make([]interface{}, 2+len(members))
+	args[0] = "GEOHASH"
+	args[1] = key
+	for i, member := range members {
+		args[2+i] = member
+	}
+	cmd := NewStringSliceCmd(args...)
+	c.Process(cmd)
+	return cmd
+}
diff --git a/commands_test.go b/commands_test.go
index f10cfc3d..35527969 100644
--- a/commands_test.go
+++ b/commands_test.go
@@ -2522,63 +2522,168 @@ var _ = Describe("Commands", func() {
 	})
 
 	Describe("Geo add and radius search", func() {
+
 		It("should add one geo location", func() {
-			geoAdd := client.GeoAdd("Sicily", &redis.GeoLocation{Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"})
+			geoAdd := client.GeoAdd(
+				"Sicily",
+				&redis.GeoLocation{Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"},
+			)
 			Expect(geoAdd.Err()).NotTo(HaveOccurred())
 			Expect(geoAdd.Val()).To(Equal(int64(1)))
 		})
 
 		It("should add multiple geo locations", func() {
-			geoAdd := client.GeoAdd("Sicily", &redis.GeoLocation{Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"},
-				&redis.GeoLocation{Longitude: 15.087269, Latitude: 37.502669, Name: "Catania"})
+			geoAdd := client.GeoAdd(
+				"Sicily",
+				&redis.GeoLocation{Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"},
+				&redis.GeoLocation{Longitude: 15.087269, Latitude: 37.502669, Name: "Catania"},
+			)
 			Expect(geoAdd.Err()).NotTo(HaveOccurred())
 			Expect(geoAdd.Val()).To(Equal(int64(2)))
 		})
 
 		It("should search geo radius", func() {
-			geoAdd := client.GeoAdd("Sicily", &redis.GeoLocation{Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"},
-				&redis.GeoLocation{Longitude: 15.087269, Latitude: 37.502669, Name: "Catania"})
+			geoAdd := client.GeoAdd(
+				"Sicily",
+				&redis.GeoLocation{Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"},
+				&redis.GeoLocation{Longitude: 15.087269, Latitude: 37.502669, Name: "Catania"},
+			)
 			Expect(geoAdd.Err()).NotTo(HaveOccurred())
 			Expect(geoAdd.Val()).To(Equal(int64(2)))
 
-			geoRadius := client.GeoRadius(&redis.GeoRadiusQuery{Key: "Sicily", Longitude: 15, Latitude: 37, Radius: 200})
-			Expect(geoRadius.Err()).NotTo(HaveOccurred())
-			Expect(geoRadius.Val()[0].Name).To(Equal("Palermo"))
-			Expect(geoRadius.Val()[1].Name).To(Equal("Catania"))
+			res, err := client.GeoRadius("Sicily", 15, 37, &redis.GeoRadiusQuery{Radius: 200}).Result()
+			Expect(err).NotTo(HaveOccurred())
+			Expect(res).To(HaveLen(2))
+			Expect(res[0].Name).To(Equal("Palermo"))
+			Expect(res[1].Name).To(Equal("Catania"))
 		})
 
 		It("should search geo radius with options", func() {
-			locations := []*redis.GeoLocation{&redis.GeoLocation{Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"},
-				&redis.GeoLocation{Longitude: 15.087269, Latitude: 37.502669, Name: "Catania"}}
+			locations := []*redis.GeoLocation{
+				&redis.GeoLocation{Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"},
+				&redis.GeoLocation{Longitude: 15.087269, Latitude: 37.502669, Name: "Catania"},
+			}
+			geoAdd := client.GeoAdd("Sicily", locations...)
+			Expect(geoAdd.Err()).NotTo(HaveOccurred())
+			Expect(geoAdd.Val()).To(Equal(int64(2)))
+
+			res, err := client.GeoRadius("Sicily", 15, 37, &redis.GeoRadiusQuery{
+				Radius:      200,
+				Unit:        "km",
+				WithGeoHash: true,
+				WithCoord:   true,
+				WithDist:    true,
+				Count:       2,
+				Sort:        "ASC",
+			}).Result()
+			Expect(err).NotTo(HaveOccurred())
+			Expect(res).To(HaveLen(2))
+			Expect(res[1].Name).To(Equal("Palermo"))
+			Expect(res[1].Distance).To(Equal(190.4424))
+			Expect(res[1].GeoHash).To(Equal(int64(3479099956230698)))
+			Expect(res[1].Longitude).To(Equal(13.361389338970184))
+			Expect(res[1].Latitude).To(Equal(38.115556395496299))
+			Expect(res[0].Name).To(Equal("Catania"))
+			Expect(res[0].Distance).To(Equal(56.4413))
+			Expect(res[0].GeoHash).To(Equal(int64(3479447370796909)))
+			Expect(res[0].Longitude).To(Equal(15.087267458438873))
+			Expect(res[0].Latitude).To(Equal(37.50266842333162))
+		})
+
+		It("should search geo radius by member with options", func() {
+			locations := []*redis.GeoLocation{
+				&redis.GeoLocation{Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"},
+				&redis.GeoLocation{Longitude: 15.087269, Latitude: 37.502669, Name: "Catania"},
+			}
 
 			geoAdd := client.GeoAdd("Sicily", locations...)
 			Expect(geoAdd.Err()).NotTo(HaveOccurred())
 			Expect(geoAdd.Val()).To(Equal(int64(2)))
 
-			geoRadius := client.GeoRadius(&redis.GeoRadiusQuery{Key: "Sicily", Longitude: 15, Latitude: 37, Radius: 200, Unit: "km", WithGeoHash: true, WithCoordinates: true, WithDistance: true, Count: 2, Sort: "ASC"})
-			Expect(geoRadius.Err()).NotTo(HaveOccurred())
-			Expect(geoRadius.Val()[1].Name).To(Equal("Palermo"))
-			Expect(geoRadius.Val()[1].Distance).To(Equal(190.4424))
-			Expect(geoRadius.Val()[1].GeoHash).To(Equal(int64(3479099956230698)))
-			Expect(geoRadius.Val()[1].Longitude).To(Equal(13.361389338970184))
-			Expect(geoRadius.Val()[1].Latitude).To(Equal(38.115556395496299))
-			Expect(geoRadius.Val()[0].Name).To(Equal("Catania"))
-			Expect(geoRadius.Val()[0].Distance).To(Equal(56.4413))
-			Expect(geoRadius.Val()[0].GeoHash).To(Equal(int64(3479447370796909)))
-			Expect(geoRadius.Val()[0].Longitude).To(Equal(15.087267458438873))
-			Expect(geoRadius.Val()[0].Latitude).To(Equal(37.50266842333162))
+			res, err := client.GeoRadiusByMember("Sicily", "Catania", &redis.GeoRadiusQuery{
+				Radius:      200,
+				Unit:        "km",
+				WithGeoHash: true,
+				WithCoord:   true,
+				WithDist:    true,
+				Count:       2,
+				Sort:        "ASC",
+			}).Result()
+			Expect(err).NotTo(HaveOccurred())
+			Expect(res).To(HaveLen(2))
+			Expect(res[0].Name).To(Equal("Catania"))
+			Expect(res[0].Distance).To(Equal(0.0))
+			Expect(res[0].GeoHash).To(Equal(int64(3479447370796909)))
+			Expect(res[0].Longitude).To(Equal(15.087267458438873))
+			Expect(res[0].Latitude).To(Equal(37.50266842333162))
+			Expect(res[1].Name).To(Equal("Palermo"))
+			Expect(res[1].Distance).To(Equal(166.2742))
+			Expect(res[1].GeoHash).To(Equal(int64(3479099956230698)))
+			Expect(res[1].Longitude).To(Equal(13.361389338970184))
+			Expect(res[1].Latitude).To(Equal(38.115556395496299))
 		})
 
 		It("should search geo radius with no results", func() {
-			geoAdd := client.GeoAdd("Sicily", &redis.GeoLocation{Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"},
-				&redis.GeoLocation{Longitude: 15.087269, Latitude: 37.502669, Name: "Catania"})
+			geoAdd := client.GeoAdd("Sicily", &redis.GeoLocation{
+				Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"},
+				&redis.GeoLocation{Longitude: 15.087269, Latitude: 37.502669, Name: "Catania"},
+			)
 			Expect(geoAdd.Err()).NotTo(HaveOccurred())
 			Expect(geoAdd.Val()).To(Equal(int64(2)))
 
-			geoRadius := client.GeoRadius(&redis.GeoRadiusQuery{Key: "Sicily", Longitude: 99, Latitude: 37, Radius: 200, Unit: "km", WithGeoHash: true, WithCoordinates: true, WithDistance: true})
-			Expect(geoRadius.Err()).NotTo(HaveOccurred())
-			Expect(len(geoRadius.Val())).To(Equal(0))
+			res, err := client.GeoRadius("Sicily", 99, 37, &redis.GeoRadiusQuery{
+				Radius:      200,
+				Unit:        "km",
+				WithGeoHash: true,
+				WithCoord:   true,
+				WithDist:    true,
+			}).Result()
+			Expect(err).NotTo(HaveOccurred())
+			Expect(res).To(HaveLen(0))
 		})
+
+		It("should get geo distance with unit options", func() {
+			// From Redis CLI, note the difference in rounding in m vs
+			// km on Redis itself.
+			//
+			// GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
+			// GEODIST Sicily Palermo Catania m
+			// "166274.15156960033"
+			// GEODIST Sicily Palermo Catania km
+			// "166.27415156960032"
+			locations := []*redis.GeoLocation{
+				&redis.GeoLocation{Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"},
+				&redis.GeoLocation{Longitude: 15.087269, Latitude: 37.502669, Name: "Catania"},
+			}
+
+			geoAdd := client.GeoAdd("Sicily", locations...)
+			Expect(geoAdd.Err()).NotTo(HaveOccurred())
+			Expect(geoAdd.Val()).To(Equal(int64(2)))
+
+			geoDist := client.GeoDist("Sicily", "Palermo", "Catania", "km")
+			Expect(geoDist.Err()).NotTo(HaveOccurred())
+			Expect(geoDist.Val()).To(Equal(166.27415156960032))
+
+			geoDist = client.GeoDist("Sicily", "Palermo", "Catania", "m")
+			Expect(geoDist.Err()).NotTo(HaveOccurred())
+			Expect(geoDist.Val()).To(Equal(166274.15156960033))
+		})
+
+		It("should get geo hash in string representation", func() {
+			locations := []*redis.GeoLocation{
+				&redis.GeoLocation{Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"},
+				&redis.GeoLocation{Longitude: 15.087269, Latitude: 37.502669, Name: "Catania"},
+			}
+			geoAdd := client.GeoAdd("Sicily", locations...)
+			Expect(geoAdd.Err()).NotTo(HaveOccurred())
+			Expect(geoAdd.Val()).To(Equal(int64(2)))
+
+			res, err := client.GeoHash("Sicily", "Palermo", "Catania").Result()
+			Expect(err).NotTo(HaveOccurred())
+			Expect(res[0]).To(Equal("sqc8b49rny0"))
+			Expect(res[1]).To(Equal("sqdtr74hyu0"))
+		})
+
 	})
 
 	Describe("marshaling/unmarshaling", func() {