forked from mirror/redis
adjust the code (#1842)
* Upgrade redis-server version (#1833) * Upgrade redis-server version Signed-off-by: monkey <golang@88.com> * XAutoClaim changed the return value Signed-off-by: monkey <golang@88.com> * add cmd: geosearch, geosearchstore (#1836) * add cmd: geosearch, geosearchstore Signed-off-by: monkey92t <golang@88.com> * GeoSearchQuery and GeoSearchLocationQuery changed to pointer passing Signed-off-by: monkey92t <golang@88.com> * adjust the code, and fix #1553, #1676 Signed-off-by: monkey92t <golang@88.com>
This commit is contained in:
parent
b8245b56f9
commit
38d1749d56
2
Makefile
2
Makefile
|
@ -14,7 +14,7 @@ bench: testdeps
|
||||||
|
|
||||||
testdata/redis:
|
testdata/redis:
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
wget -qO- https://download.redis.io/releases/redis-6.2.1.tar.gz | tar xvz --strip-components=1 -C $@
|
wget -qO- https://download.redis.io/releases/redis-6.2.5.tar.gz | tar xvz --strip-components=1 -C $@
|
||||||
|
|
||||||
testdata/redis/src/redis-server: testdata/redis
|
testdata/redis/src/redis-server: testdata/redis
|
||||||
cd $< && make all
|
cd $< && make all
|
||||||
|
|
|
@ -795,7 +795,6 @@ func (c *ClusterClient) process(ctx context.Context, cmd Cmder) error {
|
||||||
_ = pipe.Process(ctx, NewCmd(ctx, "asking"))
|
_ = pipe.Process(ctx, NewCmd(ctx, "asking"))
|
||||||
_ = pipe.Process(ctx, cmd)
|
_ = pipe.Process(ctx, cmd)
|
||||||
_, lastErr = pipe.Exec(ctx)
|
_, lastErr = pipe.Exec(ctx)
|
||||||
_ = pipe.Close()
|
|
||||||
ask = false
|
ask = false
|
||||||
} else {
|
} else {
|
||||||
lastErr = node.Client.Process(ctx, cmd)
|
lastErr = node.Client.Process(ctx, cmd)
|
||||||
|
|
|
@ -515,9 +515,7 @@ var _ = Describe("ClusterClient", func() {
|
||||||
pipe = client.Pipeline().(*redis.Pipeline)
|
pipe = client.Pipeline().(*redis.Pipeline)
|
||||||
})
|
})
|
||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {})
|
||||||
Expect(pipe.Close()).NotTo(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
assertPipeline()
|
assertPipeline()
|
||||||
})
|
})
|
||||||
|
@ -527,9 +525,7 @@ var _ = Describe("ClusterClient", func() {
|
||||||
pipe = client.TxPipeline().(*redis.Pipeline)
|
pipe = client.TxPipeline().(*redis.Pipeline)
|
||||||
})
|
})
|
||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {})
|
||||||
Expect(pipe.Close()).NotTo(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
assertPipeline()
|
assertPipeline()
|
||||||
})
|
})
|
||||||
|
|
177
command.go
177
command.go
|
@ -2564,6 +2564,183 @@ func (cmd *GeoLocationCmd) readReply(rd *proto.Reader) error {
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// GeoSearchQuery is used for GEOSearch/GEOSearchStore command query.
|
||||||
|
type GeoSearchQuery struct {
|
||||||
|
Member string
|
||||||
|
|
||||||
|
// Latitude and Longitude when using FromLonLat option.
|
||||||
|
Longitude float64
|
||||||
|
Latitude float64
|
||||||
|
|
||||||
|
// Distance and unit when using ByRadius option.
|
||||||
|
// Can use m, km, ft, or mi. Default is km.
|
||||||
|
Radius float64
|
||||||
|
RadiusUnit string
|
||||||
|
|
||||||
|
// Height, width and unit when using ByBox option.
|
||||||
|
// Can be m, km, ft, or mi. Default is km.
|
||||||
|
BoxWidth float64
|
||||||
|
BoxHeight float64
|
||||||
|
BoxUnit string
|
||||||
|
|
||||||
|
// Can be ASC or DESC. Default is no sort order.
|
||||||
|
Sort string
|
||||||
|
Count int
|
||||||
|
CountAny bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeoSearchLocationQuery struct {
|
||||||
|
GeoSearchQuery
|
||||||
|
|
||||||
|
WithCoord bool
|
||||||
|
WithDist bool
|
||||||
|
WithHash bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeoSearchStoreQuery struct {
|
||||||
|
GeoSearchQuery
|
||||||
|
|
||||||
|
// When using the StoreDist option, the command stores the items in a
|
||||||
|
// sorted set populated with their distance from the center of the circle or box,
|
||||||
|
// as a floating-point number, in the same unit specified for that shape.
|
||||||
|
StoreDist bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func geoSearchLocationArgs(q *GeoSearchLocationQuery, args []interface{}) []interface{} {
|
||||||
|
args = geoSearchArgs(&q.GeoSearchQuery, args)
|
||||||
|
|
||||||
|
if q.WithCoord {
|
||||||
|
args = append(args, "withcoord")
|
||||||
|
}
|
||||||
|
if q.WithDist {
|
||||||
|
args = append(args, "withdist")
|
||||||
|
}
|
||||||
|
if q.WithHash {
|
||||||
|
args = append(args, "withhash")
|
||||||
|
}
|
||||||
|
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
func geoSearchArgs(q *GeoSearchQuery, args []interface{}) []interface{} {
|
||||||
|
if q.Member != "" {
|
||||||
|
args = append(args, "frommember", q.Member)
|
||||||
|
} else {
|
||||||
|
args = append(args, "fromlonlat", q.Longitude, q.Latitude)
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.Radius > 0 {
|
||||||
|
if q.RadiusUnit == "" {
|
||||||
|
q.RadiusUnit = "km"
|
||||||
|
}
|
||||||
|
args = append(args, "byradius", q.Radius, q.RadiusUnit)
|
||||||
|
} else {
|
||||||
|
if q.BoxUnit == "" {
|
||||||
|
q.BoxUnit = "km"
|
||||||
|
}
|
||||||
|
args = append(args, "bybox", q.BoxWidth, q.BoxHeight, q.BoxUnit)
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.Sort != "" {
|
||||||
|
args = append(args, q.Sort)
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.Count > 0 {
|
||||||
|
args = append(args, "count", q.Count)
|
||||||
|
if q.CountAny {
|
||||||
|
args = append(args, "any")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeoSearchLocationCmd struct {
|
||||||
|
baseCmd
|
||||||
|
|
||||||
|
opt *GeoSearchLocationQuery
|
||||||
|
val []GeoLocation
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Cmder = (*GeoSearchLocationCmd)(nil)
|
||||||
|
|
||||||
|
func NewGeoSearchLocationCmd(
|
||||||
|
ctx context.Context, opt *GeoSearchLocationQuery, args ...interface{},
|
||||||
|
) *GeoSearchLocationCmd {
|
||||||
|
return &GeoSearchLocationCmd{
|
||||||
|
baseCmd: baseCmd{
|
||||||
|
ctx: ctx,
|
||||||
|
args: args,
|
||||||
|
},
|
||||||
|
opt: opt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *GeoSearchLocationCmd) Val() []GeoLocation {
|
||||||
|
return cmd.val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *GeoSearchLocationCmd) Result() ([]GeoLocation, error) {
|
||||||
|
return cmd.val, cmd.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *GeoSearchLocationCmd) String() string {
|
||||||
|
return cmdString(cmd, cmd.val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *GeoSearchLocationCmd) readReply(rd *proto.Reader) error {
|
||||||
|
n, err := rd.ReadArrayLen()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.val = make([]GeoLocation, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
_, err = rd.ReadArrayLen()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var loc GeoLocation
|
||||||
|
|
||||||
|
loc.Name, err = rd.ReadString()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if cmd.opt.WithDist {
|
||||||
|
loc.Dist, err = rd.ReadFloat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cmd.opt.WithHash {
|
||||||
|
loc.GeoHash, err = rd.ReadInt()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cmd.opt.WithCoord {
|
||||||
|
if err = rd.ReadFixedArrayLen(2); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
loc.Longitude, err = rd.ReadFloat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
loc.Latitude, err = rd.ReadFloat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.val[i] = loc
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
type GeoPos struct {
|
type GeoPos struct {
|
||||||
Longitude, Latitude float64
|
Longitude, Latitude float64
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
redis "github.com/go-redis/redis/v8"
|
"github.com/go-redis/redis/v8"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
39
commands.go
39
commands.go
|
@ -244,6 +244,7 @@ type Cmdable interface {
|
||||||
XTrimMinIDApprox(ctx context.Context, key string, minID string, limit int64) *IntCmd
|
XTrimMinIDApprox(ctx context.Context, key string, minID string, limit int64) *IntCmd
|
||||||
XInfoGroups(ctx context.Context, key string) *XInfoGroupsCmd
|
XInfoGroups(ctx context.Context, key string) *XInfoGroupsCmd
|
||||||
XInfoStream(ctx context.Context, key string) *XInfoStreamCmd
|
XInfoStream(ctx context.Context, key string) *XInfoStreamCmd
|
||||||
|
XInfoStreamFull(ctx context.Context, key string, count int) *XInfoStreamFullCmd
|
||||||
XInfoConsumers(ctx context.Context, key string, group string) *XInfoConsumersCmd
|
XInfoConsumers(ctx context.Context, key string, group string) *XInfoConsumersCmd
|
||||||
|
|
||||||
BZPopMax(ctx context.Context, timeout time.Duration, keys ...string) *ZWithKeyCmd
|
BZPopMax(ctx context.Context, timeout time.Duration, keys ...string) *ZWithKeyCmd
|
||||||
|
@ -304,6 +305,8 @@ type Cmdable interface {
|
||||||
ClientList(ctx context.Context) *StringCmd
|
ClientList(ctx context.Context) *StringCmd
|
||||||
ClientPause(ctx context.Context, dur time.Duration) *BoolCmd
|
ClientPause(ctx context.Context, dur time.Duration) *BoolCmd
|
||||||
ClientID(ctx context.Context) *IntCmd
|
ClientID(ctx context.Context) *IntCmd
|
||||||
|
ClientUnblock(ctx context.Context, id int64) *IntCmd
|
||||||
|
ClientUnblockWithError(ctx context.Context, id int64) *IntCmd
|
||||||
ConfigGet(ctx context.Context, parameter string) *MapStringStringCmd
|
ConfigGet(ctx context.Context, parameter string) *MapStringStringCmd
|
||||||
ConfigResetStat(ctx context.Context) *StatusCmd
|
ConfigResetStat(ctx context.Context) *StatusCmd
|
||||||
ConfigSet(ctx context.Context, parameter, value string) *StatusCmd
|
ConfigSet(ctx context.Context, parameter, value string) *StatusCmd
|
||||||
|
@ -320,6 +323,7 @@ type Cmdable interface {
|
||||||
ShutdownSave(ctx context.Context) *StatusCmd
|
ShutdownSave(ctx context.Context) *StatusCmd
|
||||||
ShutdownNoSave(ctx context.Context) *StatusCmd
|
ShutdownNoSave(ctx context.Context) *StatusCmd
|
||||||
SlaveOf(ctx context.Context, host, port string) *StatusCmd
|
SlaveOf(ctx context.Context, host, port string) *StatusCmd
|
||||||
|
SlowLogGet(ctx context.Context, num int64) *SlowLogCmd
|
||||||
Time(ctx context.Context) *TimeCmd
|
Time(ctx context.Context) *TimeCmd
|
||||||
DebugObject(ctx context.Context, key string) *StringCmd
|
DebugObject(ctx context.Context, key string) *StringCmd
|
||||||
ReadOnly(ctx context.Context) *StatusCmd
|
ReadOnly(ctx context.Context) *StatusCmd
|
||||||
|
@ -364,6 +368,9 @@ type Cmdable interface {
|
||||||
GeoRadiusStore(ctx context.Context, key string, longitude, latitude float64, query *GeoRadiusQuery) *IntCmd
|
GeoRadiusStore(ctx context.Context, key string, longitude, latitude float64, query *GeoRadiusQuery) *IntCmd
|
||||||
GeoRadiusByMember(ctx context.Context, key, member string, query *GeoRadiusQuery) *GeoLocationCmd
|
GeoRadiusByMember(ctx context.Context, key, member string, query *GeoRadiusQuery) *GeoLocationCmd
|
||||||
GeoRadiusByMemberStore(ctx context.Context, key, member string, query *GeoRadiusQuery) *IntCmd
|
GeoRadiusByMemberStore(ctx context.Context, key, member string, query *GeoRadiusQuery) *IntCmd
|
||||||
|
GeoSearch(ctx context.Context, key string, q *GeoSearchQuery) *StringSliceCmd
|
||||||
|
GeoSearchLocation(ctx context.Context, key string, q *GeoSearchLocationQuery) *GeoSearchLocationCmd
|
||||||
|
GeoSearchStore(ctx context.Context, key, store string, q *GeoSearchStoreQuery) *IntCmd
|
||||||
GeoDist(ctx context.Context, key string, member1, member2, unit string) *FloatCmd
|
GeoDist(ctx context.Context, key string, member1, member2, unit string) *FloatCmd
|
||||||
GeoHash(ctx context.Context, key string, members ...string) *StringSliceCmd
|
GeoHash(ctx context.Context, key string, members ...string) *StringSliceCmd
|
||||||
}
|
}
|
||||||
|
@ -3240,6 +3247,38 @@ func (c cmdable) GeoRadiusByMemberStore(
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c cmdable) GeoSearch(ctx context.Context, key string, q *GeoSearchQuery) *StringSliceCmd {
|
||||||
|
args := make([]interface{}, 0, 13)
|
||||||
|
args = append(args, "geosearch", key)
|
||||||
|
args = geoSearchArgs(q, args)
|
||||||
|
cmd := NewStringSliceCmd(ctx, args...)
|
||||||
|
_ = c(ctx, cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c cmdable) GeoSearchLocation(
|
||||||
|
ctx context.Context, key string, q *GeoSearchLocationQuery,
|
||||||
|
) *GeoSearchLocationCmd {
|
||||||
|
args := make([]interface{}, 0, 16)
|
||||||
|
args = append(args, "geosearch", key)
|
||||||
|
args = geoSearchLocationArgs(q, args)
|
||||||
|
cmd := NewGeoSearchLocationCmd(ctx, q, args...)
|
||||||
|
_ = c(ctx, cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c cmdable) GeoSearchStore(ctx context.Context, key, store string, q *GeoSearchStoreQuery) *IntCmd {
|
||||||
|
args := make([]interface{}, 0, 15)
|
||||||
|
args = append(args, "geosearchstore", store, key)
|
||||||
|
args = geoSearchArgs(&q.GeoSearchQuery, args)
|
||||||
|
if q.StoreDist {
|
||||||
|
args = append(args, "storedist")
|
||||||
|
}
|
||||||
|
cmd := NewIntCmd(ctx, args...)
|
||||||
|
_ = c(ctx, cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
func (c cmdable) GeoDist(
|
func (c cmdable) GeoDist(
|
||||||
ctx context.Context, key string, member1, member2, unit string,
|
ctx context.Context, key string, member1, member2, unit string,
|
||||||
) *FloatCmd {
|
) *FloatCmd {
|
||||||
|
|
209
commands_test.go
209
commands_test.go
|
@ -4657,7 +4657,7 @@ var _ = Describe("Commands", func() {
|
||||||
}
|
}
|
||||||
msgs, start, err := client.XAutoClaim(ctx, xca).Result()
|
msgs, start, err := client.XAutoClaim(ctx, xca).Result()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(start).To(Equal("2-0"))
|
Expect(start).To(Equal("3-0"))
|
||||||
Expect(msgs).To(Equal([]redis.XMessage{{
|
Expect(msgs).To(Equal([]redis.XMessage{{
|
||||||
ID: "1-0",
|
ID: "1-0",
|
||||||
Values: map[string]interface{}{"uno": "un"},
|
Values: map[string]interface{}{"uno": "un"},
|
||||||
|
@ -4669,19 +4669,16 @@ var _ = Describe("Commands", func() {
|
||||||
xca.Start = start
|
xca.Start = start
|
||||||
msgs, start, err = client.XAutoClaim(ctx, xca).Result()
|
msgs, start, err = client.XAutoClaim(ctx, xca).Result()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(start).To(Equal("3-0"))
|
Expect(start).To(Equal("0-0"))
|
||||||
Expect(msgs).To(Equal([]redis.XMessage{{
|
Expect(msgs).To(Equal([]redis.XMessage{{
|
||||||
ID: "2-0",
|
|
||||||
Values: map[string]interface{}{"dos": "deux"},
|
|
||||||
}, {
|
|
||||||
ID: "3-0",
|
ID: "3-0",
|
||||||
Values: map[string]interface{}{"tres": "troix"},
|
Values: map[string]interface{}{"tres": "troix"},
|
||||||
}}))
|
}}))
|
||||||
|
|
||||||
ids, start, err := client.XAutoClaimJustID(ctx, xca).Result()
|
ids, start, err := client.XAutoClaimJustID(ctx, xca).Result()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(start).To(Equal("3-0"))
|
Expect(start).To(Equal("0-0"))
|
||||||
Expect(ids).To(Equal([]string{"2-0", "3-0"}))
|
Expect(ids).To(Equal([]string{"3-0"}))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should XClaim", func() {
|
It("should XClaim", func() {
|
||||||
|
@ -5167,6 +5164,204 @@ var _ = Describe("Commands", func() {
|
||||||
nil,
|
nil,
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("should geo search", func() {
|
||||||
|
q := &redis.GeoSearchQuery{
|
||||||
|
Member: "Catania",
|
||||||
|
BoxWidth: 400,
|
||||||
|
BoxHeight: 100,
|
||||||
|
BoxUnit: "km",
|
||||||
|
Sort: "asc",
|
||||||
|
}
|
||||||
|
val, err := client.GeoSearch(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]string{"Catania"}))
|
||||||
|
|
||||||
|
q.BoxHeight = 400
|
||||||
|
val, err = client.GeoSearch(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]string{"Catania", "Palermo"}))
|
||||||
|
|
||||||
|
q.Count = 1
|
||||||
|
val, err = client.GeoSearch(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]string{"Catania"}))
|
||||||
|
|
||||||
|
q.CountAny = true
|
||||||
|
val, err = client.GeoSearch(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]string{"Palermo"}))
|
||||||
|
|
||||||
|
q = &redis.GeoSearchQuery{
|
||||||
|
Member: "Catania",
|
||||||
|
Radius: 100,
|
||||||
|
RadiusUnit: "km",
|
||||||
|
Sort: "asc",
|
||||||
|
}
|
||||||
|
val, err = client.GeoSearch(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]string{"Catania"}))
|
||||||
|
|
||||||
|
q.Radius = 400
|
||||||
|
val, err = client.GeoSearch(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]string{"Catania", "Palermo"}))
|
||||||
|
|
||||||
|
q.Count = 1
|
||||||
|
val, err = client.GeoSearch(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]string{"Catania"}))
|
||||||
|
|
||||||
|
q.CountAny = true
|
||||||
|
val, err = client.GeoSearch(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]string{"Palermo"}))
|
||||||
|
|
||||||
|
q = &redis.GeoSearchQuery{
|
||||||
|
Longitude: 15,
|
||||||
|
Latitude: 37,
|
||||||
|
BoxWidth: 200,
|
||||||
|
BoxHeight: 200,
|
||||||
|
BoxUnit: "km",
|
||||||
|
Sort: "asc",
|
||||||
|
}
|
||||||
|
val, err = client.GeoSearch(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]string{"Catania"}))
|
||||||
|
|
||||||
|
q.BoxWidth, q.BoxHeight = 400, 400
|
||||||
|
val, err = client.GeoSearch(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]string{"Catania", "Palermo"}))
|
||||||
|
|
||||||
|
q.Count = 1
|
||||||
|
val, err = client.GeoSearch(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]string{"Catania"}))
|
||||||
|
|
||||||
|
q.CountAny = true
|
||||||
|
val, err = client.GeoSearch(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]string{"Palermo"}))
|
||||||
|
|
||||||
|
q = &redis.GeoSearchQuery{
|
||||||
|
Longitude: 15,
|
||||||
|
Latitude: 37,
|
||||||
|
Radius: 100,
|
||||||
|
RadiusUnit: "km",
|
||||||
|
Sort: "asc",
|
||||||
|
}
|
||||||
|
val, err = client.GeoSearch(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]string{"Catania"}))
|
||||||
|
|
||||||
|
q.Radius = 200
|
||||||
|
val, err = client.GeoSearch(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]string{"Catania", "Palermo"}))
|
||||||
|
|
||||||
|
q.Count = 1
|
||||||
|
val, err = client.GeoSearch(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]string{"Catania"}))
|
||||||
|
|
||||||
|
q.CountAny = true
|
||||||
|
val, err = client.GeoSearch(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]string{"Palermo"}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should geo search with options", func() {
|
||||||
|
q := &redis.GeoSearchLocationQuery{
|
||||||
|
GeoSearchQuery: redis.GeoSearchQuery{
|
||||||
|
Longitude: 15,
|
||||||
|
Latitude: 37,
|
||||||
|
Radius: 200,
|
||||||
|
RadiusUnit: "km",
|
||||||
|
Sort: "asc",
|
||||||
|
},
|
||||||
|
WithHash: true,
|
||||||
|
WithDist: true,
|
||||||
|
WithCoord: true,
|
||||||
|
}
|
||||||
|
val, err := client.GeoSearchLocation(ctx, "Sicily", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal([]redis.GeoLocation{
|
||||||
|
{
|
||||||
|
Name: "Catania",
|
||||||
|
Longitude: 15.08726745843887329,
|
||||||
|
Latitude: 37.50266842333162032,
|
||||||
|
Dist: 56.4413,
|
||||||
|
GeoHash: 3479447370796909,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Palermo",
|
||||||
|
Longitude: 13.36138933897018433,
|
||||||
|
Latitude: 38.11555639549629859,
|
||||||
|
Dist: 190.4424,
|
||||||
|
GeoHash: 3479099956230698,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should geo search store", func() {
|
||||||
|
q := &redis.GeoSearchStoreQuery{
|
||||||
|
GeoSearchQuery: redis.GeoSearchQuery{
|
||||||
|
Longitude: 15,
|
||||||
|
Latitude: 37,
|
||||||
|
Radius: 200,
|
||||||
|
RadiusUnit: "km",
|
||||||
|
Sort: "asc",
|
||||||
|
},
|
||||||
|
StoreDist: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := client.GeoSearchStore(ctx, "Sicily", "key1", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal(int64(2)))
|
||||||
|
|
||||||
|
q.StoreDist = true
|
||||||
|
val, err = client.GeoSearchStore(ctx, "Sicily", "key2", q).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(val).To(Equal(int64(2)))
|
||||||
|
|
||||||
|
loc, err := client.GeoSearchLocation(ctx, "key1", &redis.GeoSearchLocationQuery{
|
||||||
|
GeoSearchQuery: q.GeoSearchQuery,
|
||||||
|
WithCoord: true,
|
||||||
|
WithDist: true,
|
||||||
|
WithHash: true,
|
||||||
|
}).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(loc).To(Equal([]redis.GeoLocation{
|
||||||
|
{
|
||||||
|
Name: "Catania",
|
||||||
|
Longitude: 15.08726745843887329,
|
||||||
|
Latitude: 37.50266842333162032,
|
||||||
|
Dist: 56.4413,
|
||||||
|
GeoHash: 3479447370796909,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Palermo",
|
||||||
|
Longitude: 13.36138933897018433,
|
||||||
|
Latitude: 38.11555639549629859,
|
||||||
|
Dist: 190.4424,
|
||||||
|
GeoHash: 3479099956230698,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
v, err := client.ZRangeWithScores(ctx, "key2", 0, -1).Result()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(v).To(Equal([]redis.Z{
|
||||||
|
{
|
||||||
|
Score: 56.441257870158204,
|
||||||
|
Member: "Catania",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Score: 190.44242984775784,
|
||||||
|
Member: "Palermo",
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Describe("marshaling/unmarshaling", func() {
|
Describe("marshaling/unmarshaling", func() {
|
||||||
|
|
|
@ -60,21 +60,21 @@ func (c *ClusterClient) SwapNodes(ctx context.Context, key string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (state *clusterState) IsConsistent(ctx context.Context) bool {
|
func (c *clusterState) IsConsistent(ctx context.Context) bool {
|
||||||
if len(state.Masters) < 3 {
|
if len(c.Masters) < 3 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, master := range state.Masters {
|
for _, master := range c.Masters {
|
||||||
s := master.Client.Info(ctx, "replication").Val()
|
s := master.Client.Info(ctx, "replication").Val()
|
||||||
if !strings.Contains(s, "role:master") {
|
if !strings.Contains(s, "role:master") {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(state.Slaves) < 3 {
|
if len(c.Slaves) < 3 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, slave := range state.Slaves {
|
for _, slave := range c.Slaves {
|
||||||
s := slave.Client.Info(ctx, "replication").Val()
|
s := slave.Client.Info(ctx, "replication").Val()
|
||||||
if !strings.Contains(s, "role:slave") {
|
if !strings.Contains(s, "role:slave") {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
type writer interface {
|
type writer interface {
|
||||||
io.Writer
|
io.Writer
|
||||||
io.ByteWriter
|
io.ByteWriter
|
||||||
// io.StringWriter
|
// WriteString implement io.StringWriter.
|
||||||
WriteString(s string) (n int, err error)
|
WriteString(s string) (n int, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -247,7 +247,10 @@ func setupTCPConn(u *url.URL) (*Options, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.Scheme == "rediss" {
|
if u.Scheme == "rediss" {
|
||||||
o.TLSConfig = &tls.Config{ServerName: h}
|
o.TLSConfig = &tls.Config{
|
||||||
|
ServerName: h,
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return o, nil
|
return o, nil
|
||||||
|
|
40
pipeline.go
40
pipeline.go
|
@ -3,8 +3,6 @@ package redis
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/go-redis/redis/v8/internal/pool"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type pipelineExecer func(context.Context, []Cmder) error
|
type pipelineExecer func(context.Context, []Cmder) error
|
||||||
|
@ -26,8 +24,7 @@ type Pipeliner interface {
|
||||||
StatefulCmdable
|
StatefulCmdable
|
||||||
Do(ctx context.Context, args ...interface{}) *Cmd
|
Do(ctx context.Context, args ...interface{}) *Cmd
|
||||||
Process(ctx context.Context, cmd Cmder) error
|
Process(ctx context.Context, cmd Cmder) error
|
||||||
Close() error
|
Discard()
|
||||||
Discard() error
|
|
||||||
Exec(ctx context.Context) ([]Cmder, error)
|
Exec(ctx context.Context) ([]Cmder, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,9 +40,8 @@ type Pipeline struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
exec pipelineExecer
|
exec pipelineExecer
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
cmds []Cmder
|
cmds []Cmder
|
||||||
closed bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Pipeline) init() {
|
func (c *Pipeline) init() {
|
||||||
|
@ -67,29 +63,11 @@ func (c *Pipeline) Process(ctx context.Context, cmd Cmder) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the pipeline, releasing any open resources.
|
|
||||||
func (c *Pipeline) Close() error {
|
|
||||||
c.mu.Lock()
|
|
||||||
_ = c.discard()
|
|
||||||
c.closed = true
|
|
||||||
c.mu.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Discard resets the pipeline and discards queued commands.
|
// Discard resets the pipeline and discards queued commands.
|
||||||
func (c *Pipeline) Discard() error {
|
func (c *Pipeline) Discard() {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
err := c.discard()
|
|
||||||
c.mu.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Pipeline) discard() error {
|
|
||||||
if c.closed {
|
|
||||||
return pool.ErrClosed
|
|
||||||
}
|
|
||||||
c.cmds = c.cmds[:0]
|
c.cmds = c.cmds[:0]
|
||||||
return nil
|
c.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exec executes all previously queued commands using one
|
// Exec executes all previously queued commands using one
|
||||||
|
@ -101,10 +79,6 @@ func (c *Pipeline) Exec(ctx context.Context) ([]Cmder, error) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
if c.closed {
|
|
||||||
return nil, pool.ErrClosed
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(c.cmds) == 0 {
|
if len(c.cmds) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -119,9 +93,7 @@ func (c *Pipeline) Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]C
|
||||||
if err := fn(c); err != nil {
|
if err := fn(c); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cmds, err := c.Exec(ctx)
|
return c.Exec(ctx)
|
||||||
_ = c.Close()
|
|
||||||
return cmds, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Pipeline) Pipeline() Pipeliner {
|
func (c *Pipeline) Pipeline() Pipeliner {
|
||||||
|
|
|
@ -72,7 +72,6 @@ var _ = Describe("pool", func() {
|
||||||
Expect(cmds).To(HaveLen(1))
|
Expect(cmds).To(HaveLen(1))
|
||||||
Expect(ping.Err()).NotTo(HaveOccurred())
|
Expect(ping.Err()).NotTo(HaveOccurred())
|
||||||
Expect(ping.Val()).To(Equal("PONG"))
|
Expect(ping.Val()).To(Equal("PONG"))
|
||||||
Expect(pipe.Close()).NotTo(HaveOccurred())
|
|
||||||
})
|
})
|
||||||
|
|
||||||
pool := client.Pool()
|
pool := client.Pool()
|
||||||
|
|
|
@ -136,17 +136,6 @@ var _ = Describe("Client", func() {
|
||||||
Expect(client.Ping(ctx).Err()).NotTo(HaveOccurred())
|
Expect(client.Ping(ctx).Err()).NotTo(HaveOccurred())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should close pipeline without closing the client", func() {
|
|
||||||
pipeline := client.Pipeline()
|
|
||||||
Expect(pipeline.Close()).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
pipeline.Ping(ctx)
|
|
||||||
_, err := pipeline.Exec(ctx)
|
|
||||||
Expect(err).To(MatchError("redis: client is closed"))
|
|
||||||
|
|
||||||
Expect(client.Ping(ctx).Err()).NotTo(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
It("should close pubsub when client is closed", func() {
|
It("should close pubsub when client is closed", func() {
|
||||||
pubsub := client.Subscribe(ctx)
|
pubsub := client.Subscribe(ctx)
|
||||||
Expect(client.Close()).NotTo(HaveOccurred())
|
Expect(client.Close()).NotTo(HaveOccurred())
|
||||||
|
@ -157,12 +146,6 @@ var _ = Describe("Client", func() {
|
||||||
Expect(pubsub.Close()).NotTo(HaveOccurred())
|
Expect(pubsub.Close()).NotTo(HaveOccurred())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should close pipeline when client is closed", func() {
|
|
||||||
pipeline := client.Pipeline()
|
|
||||||
Expect(client.Close()).NotTo(HaveOccurred())
|
|
||||||
Expect(pipeline.Close()).NotTo(HaveOccurred())
|
|
||||||
})
|
|
||||||
|
|
||||||
It("should select DB", func() {
|
It("should select DB", func() {
|
||||||
db2 := redis.NewClient(&redis.Options{
|
db2 := redis.NewClient(&redis.Options{
|
||||||
Addr: redisAddr,
|
Addr: redisAddr,
|
||||||
|
|
2
ring.go
2
ring.go
|
@ -308,7 +308,7 @@ func (c *ringShards) Random() (*ringShard, error) {
|
||||||
return c.GetByKey(strconv.Itoa(rand.Int()))
|
return c.GetByKey(strconv.Itoa(rand.Int()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// heartbeat monitors state of each shard in the ring.
|
// Heartbeat monitors state of each shard in the ring.
|
||||||
func (c *ringShards) Heartbeat(frequency time.Duration) {
|
func (c *ringShards) Heartbeat(frequency time.Duration) {
|
||||||
ticker := time.NewTicker(frequency)
|
ticker := time.NewTicker(frequency)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
|
@ -123,7 +123,6 @@ var _ = Describe("Redis Ring", func() {
|
||||||
cmds, err := pipe.Exec(ctx)
|
cmds, err := pipe.Exec(ctx)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(cmds).To(HaveLen(100))
|
Expect(cmds).To(HaveLen(100))
|
||||||
Expect(pipe.Close()).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
for _, cmd := range cmds {
|
for _, cmd := range cmds {
|
||||||
Expect(cmd.Err()).NotTo(HaveOccurred())
|
Expect(cmd.Err()).NotTo(HaveOccurred())
|
||||||
|
|
Loading…
Reference in New Issue