Added BOUNDS command

It's now possible to get the combined minimum bounding rectangle for all
objects in a key by issuing the command "BOUNDS key".
This commit is contained in:
Josh Baker 2016-08-19 08:33:58 -07:00
parent 6e4977ac0f
commit 1d427c849f
6 changed files with 94 additions and 1 deletions

View File

@ -86,6 +86,11 @@ func (c *Collection) TotalWeight() int {
return c.weight return c.weight
} }
// Bounds returns the bounds of all the items in the collection.
func (c *Collection) Bounds() (minX, minY, maxX, maxY float64) {
return c.index.Bounds()
}
// ReplaceOrInsert adds or replaces an object in the collection and returns the fields array. // ReplaceOrInsert adds or replaces an object in the collection and returns the fields array.
// If an item with the same id is already in the collection then the new item will adopt the old item's fields. // If an item with the same id is already in the collection then the new item will adopt the old item's fields.
// The fields argument is optional. // The fields argument is optional.

View File

@ -333,7 +333,7 @@ func (c *Controller) handleInputCommand(conn *server.Conn, msg *server.Message,
if c.config.ReadOnly { if c.config.ReadOnly {
return writeErr(errors.New("read only")) return writeErr(errors.New("read only"))
} }
case "get", "keys", "scan", "nearby", "within", "intersects", "hooks", "search", "ttl": case "get", "keys", "scan", "nearby", "within", "intersects", "hooks", "search", "ttl", "bounds":
// read operations // read operations
c.mu.RLock() c.mu.RLock()
defer c.mu.RUnlock() defer c.mu.RUnlock()
@ -444,6 +444,8 @@ func (c *Controller) command(msg *server.Message, w io.Writer) (res string, d co
res, err = c.cmdIntersects(msg) res, err = c.cmdIntersects(msg)
case "search": case "search":
res, err = c.cmdSearch(msg) res, err = c.cmdSearch(msg)
case "bounds":
res, err = c.cmdBounds(msg)
case "get": case "get":
res, err = c.cmdGet(msg) res, err = c.cmdGet(msg)
case "keys": case "keys":

View File

@ -47,6 +47,63 @@ func orderFields(fmap map[string]int, fields []float64) []fvt {
sort.Sort(byField(fvs)) sort.Sort(byField(fvs))
return fvs return fvs
} }
func (c *Controller) cmdBounds(msg *server.Message) (string, error) {
start := time.Now()
vs := msg.Values[1:]
var ok bool
var key string
if vs, key, ok = tokenval(vs); !ok || key == "" {
return "", errInvalidNumberOfArguments
}
if len(vs) != 0 {
return "", errInvalidNumberOfArguments
}
col := c.getCol(key)
if col == nil {
if msg.OutputType == server.RESP {
return "$-1\r\n", nil
}
return "", errKeyNotFound
}
vals := make([]resp.Value, 0, 2)
var buf bytes.Buffer
if msg.OutputType == server.JSON {
buf.WriteString(`{"ok":true`)
}
bbox := geojson.New2DBBox(col.Bounds())
if msg.OutputType == server.JSON {
buf.WriteString(`,"bounds":`)
buf.WriteString(bbox.ExternalJSON())
} else {
vals = append(vals, resp.ArrayValue([]resp.Value{
resp.ArrayValue([]resp.Value{
resp.FloatValue(bbox.Min.Y),
resp.FloatValue(bbox.Min.X),
}),
resp.ArrayValue([]resp.Value{
resp.FloatValue(bbox.Max.Y),
resp.FloatValue(bbox.Max.X),
}),
}))
}
switch msg.OutputType {
case server.JSON:
buf.WriteString(`,"elapsed":"` + time.Now().Sub(start).String() + "\"}")
return buf.String(), nil
case server.RESP:
var oval resp.Value
oval = vals[0]
data, err := oval.MarshalRESP()
if err != nil {
return "", err
}
return string(data), nil
}
return "", nil
}
func (c *Controller) cmdGet(msg *server.Message) (string, error) { func (c *Controller) cmdGet(msg *server.Message) (string, error) {
start := time.Now() start := time.Now()

View File

@ -176,6 +176,18 @@
"since": "1.0.0", "since": "1.0.0",
"group": "keys" "group": "keys"
}, },
"BOUNDS": {
"summary": "Get the combined bounds of all the objects in a key",
"complexity": "O(1)",
"arguments":[
{
"name": "key",
"type": "string"
}
],
"since": "1.3.0",
"group": "keys"
},
"GET": { "GET": {
"summary": "Get the object of an id", "summary": "Get the object of an id",
"complexity": "O(1)", "complexity": "O(1)",

View File

@ -338,6 +338,18 @@ var commandsJSON = `{
"since": "1.0.0", "since": "1.0.0",
"group": "keys" "group": "keys"
}, },
"BOUNDS": {
"summary": "Get the combined bounds of all the objects in a key",
"complexity": "O(1)",
"arguments":[
{
"name": "key",
"type": "string"
}
],
"since": "1.3.0",
"group": "keys"
},
"GET": { "GET": {
"summary": "Get the object of an id", "summary": "Get the object of an id",
"complexity": "O(1)", "complexity": "O(1)",

View File

@ -99,6 +99,11 @@ func (ix *Index) Count() int {
return count return count
} }
// Bounds returns the minimum bounding rectangle of all items in the index.
func (ix *Index) Bounds() (MinX, MinY, MaxX, MaxY float64) {
return ix.r.Bounds()
}
// RemoveAll removes all items from the index. // RemoveAll removes all items from the index.
func (ix *Index) RemoveAll() { func (ix *Index) RemoveAll() {
ix.r.RemoveAll() ix.r.RemoveAll()