Repository: go-redis/redis
Pull request #1119 (2019-08-09 12:05:15)

Add writing version of GeoRadius commands
Fixes https://github.com/go-redis/redis/issues/1083


Patch:
diff --git a/command.go b/command.go
index b8f6977..833a12b 100644
--- a/command.go
+++ b/command.go
@@ -1580,6 +1580,13 @@ type GeoLocationCmd struct {
 var _ Cmder = (*GeoLocationCmd)(nil)
 
 func NewGeoLocationCmd(q *GeoRadiusQuery, args ...interface{}) *GeoLocationCmd {
+	return &GeoLocationCmd{
+		baseCmd: baseCmd{_args: geoLocationArgs(q, args...)},
+		q:       q,
+	}
+}
+
+func geoLocationArgs(q *GeoRadiusQuery, args ...interface{}) []interface{} {
 	args = append(args, q.Radius)
 	if q.Unit != "" {
 		args = append(args, q.Unit)
@@ -1609,10 +1616,7 @@ func NewGeoLocationCmd(q *GeoRadiusQuery, args ...interface{}) *GeoLocationCmd {
 		args = append(args, "storedist")
 		args = append(args, q.StoreDist)
 	}
-	return &GeoLocationCmd{
-		baseCmd: baseCmd{_args: args},
-		q:       q,
-	}
+	return args
 }
 
 func (cmd *GeoLocationCmd) Val() []GeoLocation {
diff --git a/commands.go b/commands.go
index ec2d4c9..51bad63 100644
--- a/commands.go
+++ b/commands.go
@@ -282,9 +282,9 @@ type Cmdable interface {
 	GeoAdd(key string, geoLocation ...*GeoLocation) *IntCmd
 	GeoPos(key string, members ...string) *GeoPosCmd
 	GeoRadius(key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd
-	GeoRadiusRO(key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd
+	GeoRadiusStore(key string, longitude, latitude float64, query *GeoRadiusQuery) *IntCmd
 	GeoRadiusByMember(key, member string, query *GeoRadiusQuery) *GeoLocationCmd
-	GeoRadiusByMemberRO(key, member string, query *GeoRadiusQuery) *GeoLocationCmd
+	GeoRadiusByMemberStore(key, member string, query *GeoRadiusQuery) *IntCmd
 	GeoDist(key string, member1, member2, unit string) *FloatCmd
 	GeoHash(key string, members ...string) *StringSliceCmd
 	ReadOnly() *StatusCmd
@@ -2513,26 +2513,48 @@ func (c cmdable) GeoAdd(key string, geoLocation ...*GeoLocation) *IntCmd {
 	return cmd
 }
 
+// GeoRadius is a read-only GEORADIUS_RO command.
 func (c cmdable) GeoRadius(key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd {
-	cmd := NewGeoLocationCmd(query, "georadius", key, longitude, latitude)
+	cmd := NewGeoLocationCmd(query, "georadius_ro", key, longitude, latitude)
+	if query.Store != "" || query.StoreDist != "" {
+		cmd.setErr(errors.New("GeoRadius does not support Store or StoreDist"))
+		return cmd
+	}
 	_ = c(cmd)
 	return cmd
 }
 
-func (c cmdable) GeoRadiusRO(key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd {
-	cmd := NewGeoLocationCmd(query, "georadius_ro", key, longitude, latitude)
+// GeoRadiusStore is a writing GEORADIUS command.
+func (c cmdable) GeoRadiusStore(key string, longitude, latitude float64, query *GeoRadiusQuery) *IntCmd {
+	args := geoLocationArgs(query, "georadius", key, longitude, latitude)
+	cmd := NewIntCmd(args...)
+	if query.Store == "" && query.StoreDist == "" {
+		cmd.setErr(errors.New("GeoRadiusStore requires Store or StoreDist"))
+		return cmd
+	}
 	_ = c(cmd)
 	return cmd
 }
 
+// GeoRadius is a read-only GEORADIUSBYMEMBER_RO command.
 func (c cmdable) GeoRadiusByMember(key, member string, query *GeoRadiusQuery) *GeoLocationCmd {
-	cmd := NewGeoLocationCmd(query, "georadiusbymember", key, member)
+	cmd := NewGeoLocationCmd(query, "georadiusbymember_ro", key, member)
+	if query.Store != "" || query.StoreDist != "" {
+		cmd.setErr(errors.New("GeoRadiusByMember does not support Store or StoreDist"))
+		return cmd
+	}
 	_ = c(cmd)
 	return cmd
 }
 
-func (c cmdable) GeoRadiusByMemberRO(key, member string, query *GeoRadiusQuery) *GeoLocationCmd {
-	cmd := NewGeoLocationCmd(query, "georadiusbymember_ro", key, member)
+// GeoRadiusByMemberStore is a writing GEORADIUSBYMEMBER command.
+func (c cmdable) GeoRadiusByMemberStore(key, member string, query *GeoRadiusQuery) *IntCmd {
+	args := geoLocationArgs(query, "georadiusbymember", key, member)
+	cmd := NewIntCmd(args...)
+	if query.Store == "" && query.StoreDist == "" {
+		cmd.setErr(errors.New("GeoRadiusByMemberStore requires Store or StoreDist"))
+		return cmd
+	}
 	_ = c(cmd)
 	return cmd
 }
diff --git a/internal/pool/pool.go b/internal/pool/pool.go
index 4cdcff2..ff94642 100644
--- a/internal/pool/pool.go
+++ b/internal/pool/pool.go
@@ -310,6 +310,12 @@ func (p *ConnPool) popIdle() *Conn {
 }
 
 func (p *ConnPool) Put(cn *Conn) {
+	if cn.rd.Buffered() > 0 {
+		internal.Logger.Printf("Conn has unread data")
+		p.Remove(cn, BadConnError{})
+		return
+	}
+
 	if !cn.pooled {
 		p.Remove(cn, nil)
 		return
diff --git a/internal/proto/reader.go b/internal/proto/reader.go
index 7a2777e..a48f1ae 100644
--- a/internal/proto/reader.go
+++ b/internal/proto/reader.go
@@ -41,6 +41,10 @@ func NewReader(rd io.Reader) *Reader {
 	}
 }
 
+func (r *Reader) Buffered() int {
+	return r.rd.Buffered()
+}
+
 func (r *Reader) Reset(rd io.Reader) {
 	r.rd.Reset(rd)
 }


Test patch:
diff --git a/commands_test.go b/commands_test.go
index d1bb6ed..c84452f 100644
--- a/commands_test.go
+++ b/commands_test.go
@@ -3733,6 +3733,46 @@ var _ = Describe("Commands", func() {
 			Expect(res[1].Name).To(Equal("Catania"))
 		})
 
+		It("should geo radius and store the result", func() {
+			n, err := client.GeoRadiusStore("Sicily", 15, 37, &redis.GeoRadiusQuery{
+				Radius: 200,
+				Store:  "result",
+			}).Result()
+			Expect(err).NotTo(HaveOccurred())
+			Expect(n).To(Equal(int64(2)))
+
+			res, err := client.ZRangeWithScores("result", 0, -1).Result()
+			Expect(err).NotTo(HaveOccurred())
+			Expect(res).To(ContainElement(redis.Z{
+				Score:  3.479099956230698e+15,
+				Member: "Palermo",
+			}))
+			Expect(res).To(ContainElement(redis.Z{
+				Score:  3.479447370796909e+15,
+				Member: "Catania",
+			}))
+		})
+
+		It("should geo radius and store dist", func() {
+			n, err := client.GeoRadiusStore("Sicily", 15, 37, &redis.GeoRadiusQuery{
+				Radius:    200,
+				StoreDist: "result",
+			}).Result()
+			Expect(err).NotTo(HaveOccurred())
+			Expect(n).To(Equal(int64(2)))
+
+			res, err := client.ZRangeWithScores("result", 0, -1).Result()
+			Expect(err).NotTo(HaveOccurred())
+			Expect(res).To(ContainElement(redis.Z{
+				Score:  190.44242984775784,
+				Member: "Palermo",
+			}))
+			Expect(res).To(ContainElement(redis.Z{
+				Score:  56.4412578701582,
+				Member: "Catania",
+			}))
+		})
+
 		It("should search geo radius with options", func() {
 			res, err := client.GeoRadius("Sicily", 15, 37, &redis.GeoRadiusQuery{
 				Radius:      200,