diff --git a/command.go b/command.go index 9cb70587..d89eab65 100644 --- a/command.go +++ b/command.go @@ -806,6 +806,10 @@ type GeoLocation struct { GeoHash int64 } +type GeoPosition struct { + Longitude, Latitude float64 +} + // GeoRadiusQuery is used with GeoRadius to query geospatial index. type GeoRadiusQuery struct { Radius float64 @@ -882,6 +886,44 @@ func (cmd *GeoLocationCmd) readReply(cn *pool.Conn) error { return nil } +type GeoPosCmd struct { + baseCmd + + positions []*GeoPosition +} + +func NewGeoPosCmd(args ...interface{}) *GeoPosCmd { + cmd := newBaseCmd(args) + return &GeoPosCmd{baseCmd: cmd} +} + +func (cmd *GeoPosCmd) Val() []*GeoPosition { + return cmd.positions +} + +func (cmd *GeoPosCmd) Result() ([]*GeoPosition, error) { + return cmd.Val(), cmd.Err() +} + +func (cmd *GeoPosCmd) String() string { + return cmdString(cmd, cmd.positions) +} + +func (cmd *GeoPosCmd) reset() { + cmd.positions = nil + cmd.err = nil +} + +func (cmd *GeoPosCmd) readReply(cn *pool.Conn) error { + reply, err := cn.Rd.ReadArrayReply(newGeoPositionSliceParser()) + if err != nil { + cmd.err = err + return err + } + cmd.positions = reply.([]*GeoPosition) + return nil +} + //------------------------------------------------------------------------------ type CommandInfo struct { diff --git a/commands.go b/commands.go index d7f22495..bcaeadad 100644 --- a/commands.go +++ b/commands.go @@ -234,6 +234,7 @@ type Cmdable interface { ClusterAddSlots(slots ...int) *StatusCmd ClusterAddSlotsRange(min, max int) *StatusCmd GeoAdd(key string, geoLocation ...*GeoLocation) *IntCmd + GeoPos(key string, name ...string) *GeoPosCmd GeoRadius(key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd GeoRadiusByMember(key, member string, query *GeoRadiusQuery) *GeoLocationCmd GeoDist(key string, member1, member2, unit string) *FloatCmd @@ -2054,6 +2055,18 @@ func (c *cmdable) GeoHash(key string, members ...string) *StringSliceCmd { return cmd } +func (c *cmdable) GeoPos(key string, names ...string) *GeoPosCmd { + args := make([]interface{}, 2+len(names)) + args[0] = "geopos" + args[1] = key + for i, name := range names { + args[2+i] = name + } + cmd := NewGeoPosCmd(args...) + c.process(cmd) + return cmd +} + //------------------------------------------------------------------------------ func (c *cmdable) Command() *CommandsInfoCmd { diff --git a/parser.go b/parser.go index 95a4297b..c1db8d0d 100644 --- a/parser.go +++ b/parser.go @@ -273,6 +273,48 @@ func newGeoLocationSliceParser(q *GeoRadiusQuery) proto.MultiBulkParse { } } +func newGeoPositionParser() proto.MultiBulkParse { + return func(rd *proto.Reader, n int64) (interface{}, error) { + var pos GeoPosition + var err error + + pos.Longitude, err = rd.ReadFloatReply() + if err != nil { + return nil, err + } + pos.Latitude, err = rd.ReadFloatReply() + if err != nil { + return nil, err + } + + return &pos, nil + } +} + +func newGeoPositionSliceParser() proto.MultiBulkParse { + return func(rd *proto.Reader, n int64) (interface{}, error) { + positions := make([]*GeoPosition, 0, n) + for i := int64(0); i < n; i++ { + v, err := rd.ReadReply(newGeoPositionParser()) + if err != nil { + // response may contain nil results + if err == Nil { + positions = append(positions, nil) + continue + } + return nil, err + } + switch vv := v.(type) { + case *GeoPosition: + positions = append(positions, vv) + default: + return nil, fmt.Errorf("got %T, expected *GeoPosition", v) + } + } + return positions, nil + } +} + func commandInfoParser(rd *proto.Reader, n int64) (interface{}, error) { var cmd CommandInfo var err error