Merge pull request #378 from rshura/fast_cursor

Fast cursor
This commit is contained in:
Josh Baker 2018-11-02 05:14:53 -07:00 committed by GitHub
commit 0ea2ec216c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 294 additions and 56 deletions

View File

@ -309,10 +309,19 @@ func (c *Collection) FieldArr() []string {
// Scan iterates though the collection ids.
func (c *Collection) Scan(desc bool,
offset uint64,
inc func(n uint64),
iterator func(id string, obj geojson.Object, fields []float64) bool,
) bool {
var keepon = true
var count uint64
inc(offset)
iter := func(key string, value interface{}) bool {
count++
if count <= offset {
return true
}
inc(1)
iitm := value.(*itemT)
keepon = iterator(iitm.id, iitm.obj, c.getFieldValues(iitm.id))
return keepon
@ -327,10 +336,19 @@ func (c *Collection) Scan(desc bool,
// ScanRange iterates though the collection starting with specified id.
func (c *Collection) ScanRange(start, end string, desc bool,
offset uint64,
inc func(n uint64),
iterator func(id string, obj geojson.Object, fields []float64) bool,
) bool {
var keepon = true
var count uint64
inc(offset)
iter := func(key string, value interface{}) bool {
count++
if count <= offset {
return true
}
inc(1)
if !desc {
if key >= end {
return false
@ -355,10 +373,19 @@ func (c *Collection) ScanRange(start, end string, desc bool,
// SearchValues iterates though the collection values.
func (c *Collection) SearchValues(desc bool,
offset uint64,
inc func(n uint64),
iterator func(id string, obj geojson.Object, fields []float64) bool,
) bool {
var keepon = true
var count uint64
inc(offset)
iter := func(item btree.Item) bool {
count++
if count <= offset {
return true
}
inc(1)
iitm := item.(*itemT)
keepon = iterator(iitm.id, iitm.obj, c.getFieldValues(iitm.id))
return keepon
@ -373,10 +400,19 @@ func (c *Collection) SearchValues(desc bool,
// SearchValuesRange iterates though the collection values.
func (c *Collection) SearchValuesRange(start, end string, desc bool,
offset uint64,
inc func(n uint64),
iterator func(id string, obj geojson.Object, fields []float64) bool,
) bool {
var keepon = true
var count uint64
inc(offset)
iter := func(item btree.Item) bool {
count++
if count <= offset {
return true
}
inc(1)
iitm := item.(*itemT)
keepon = iterator(iitm.id, iitm.obj, c.getFieldValues(iitm.id))
return keepon
@ -497,14 +533,24 @@ func (c *Collection) geoSparseInner(
// Within returns all object that are fully contained within an object or
// bounding box. Set obj to nil in order to use the bounding box.
func (c *Collection) Within(
obj geojson.Object, sparse uint8,
obj geojson.Object,
offset uint64,
sparse uint8,
inc func(n uint64),
iter func(id string, obj geojson.Object, fields []float64) bool,
) bool {
var count uint64
inc(offset)
if sparse > 0 {
return c.geoSparse(obj, sparse,
func(id string, o geojson.Object, fields []float64) (
match, ok bool,
) {
count++
if count <= offset {
return false, true
}
inc(1)
if match = o.Within(obj); match {
ok = iter(id, o, fields)
}
@ -514,6 +560,11 @@ func (c *Collection) Within(
}
return c.geoSearch(obj.Rect(),
func(id string, o geojson.Object, fields []float64) bool {
count++
if count <= offset {
return true
}
inc(1)
if o.Within(obj) {
return iter(id, o, fields)
}
@ -525,14 +576,24 @@ func (c *Collection) Within(
// Intersects returns all object that are intersect an object or bounding box.
// Set obj to nil in order to use the bounding box.
func (c *Collection) Intersects(
obj geojson.Object, sparse uint8,
obj geojson.Object,
offset uint64,
sparse uint8,
inc func(n uint64),
iter func(id string, obj geojson.Object, fields []float64) bool,
) bool {
var count uint64
inc(offset)
if sparse > 0 {
return c.geoSparse(obj, sparse,
func(id string, o geojson.Object, fields []float64) (
match, ok bool,
) {
count++
if count <= offset {
return false, true
}
inc(1)
if match = o.Intersects(obj); match {
ok = iter(id, o, fields)
}
@ -542,6 +603,11 @@ func (c *Collection) Intersects(
}
return c.geoSearch(obj.Rect(),
func(id string, o geojson.Object, fields []float64) bool {
count++
if count <= offset {
return true
}
inc(1)
if o.Intersects(obj) {
return iter(id, o, fields)
}
@ -553,14 +619,23 @@ func (c *Collection) Intersects(
// Nearby returns the nearest neighbors
func (c *Collection) Nearby(
target geojson.Object,
offset uint64,
inc func(n uint64),
iter func(id string, obj geojson.Object, fields []float64) bool,
) bool {
alive := true
center := target.Center()
var count uint64
inc(offset)
c.index.Nearby(
[]float64{center.X, center.Y},
[]float64{center.X, center.Y},
func(_, _ []float64, itemv interface{}) bool {
count++
if count <= offset {
return true
}
inc(1)
item := itemv.(*itemT)
alive = iter(item.id, item.obj, c.getFieldValues(item.id))
return alive

View File

@ -230,7 +230,7 @@ func TestCollectionScan(t *testing.T) {
}
var n int
var prevID string
c.Scan(false, func(id string, obj geojson.Object, fields []float64) bool {
c.Scan(false, 0, func(n uint64) {}, func(id string, obj geojson.Object, fields []float64) bool {
if n > 0 {
expect(t, id > prevID)
}
@ -241,7 +241,7 @@ func TestCollectionScan(t *testing.T) {
})
expect(t, n == c.Count())
n = 0
c.Scan(true, func(id string, obj geojson.Object, fields []float64) bool {
c.Scan(true, 0, func(n uint64) {}, func(id string, obj geojson.Object, fields []float64) bool {
if n > 0 {
expect(t, id < prevID)
}
@ -253,7 +253,7 @@ func TestCollectionScan(t *testing.T) {
expect(t, n == c.Count())
n = 0
c.ScanRange("0060", "0070", false,
c.ScanRange("0060", "0070", false, 0, func(n uint64) {},
func(id string, obj geojson.Object, fields []float64) bool {
if n > 0 {
expect(t, id > prevID)
@ -266,7 +266,7 @@ func TestCollectionScan(t *testing.T) {
expect(t, n == 10)
n = 0
c.ScanRange("0070", "0060", true,
c.ScanRange("0070", "0060", true, 0, func(n uint64) {},
func(id string, obj geojson.Object, fields []float64) bool {
if n > 0 {
expect(t, id < prevID)
@ -317,7 +317,7 @@ func TestCollectionSearch(t *testing.T) {
}
var n int
var prevValue string
c.SearchValues(false, func(id string, obj geojson.Object, fields []float64) bool {
c.SearchValues(false, 0, func(n uint64) {}, func(id string, obj geojson.Object, fields []float64) bool {
if n > 0 {
expect(t, obj.String() > prevValue)
}
@ -328,7 +328,7 @@ func TestCollectionSearch(t *testing.T) {
})
expect(t, n == c.Count())
n = 0
c.SearchValues(true, func(id string, obj geojson.Object, fields []float64) bool {
c.SearchValues(true, 0, func(n uint64) {}, func(id string, obj geojson.Object, fields []float64) bool {
if n > 0 {
expect(t, obj.String() < prevValue)
}
@ -340,7 +340,7 @@ func TestCollectionSearch(t *testing.T) {
expect(t, n == c.Count())
n = 0
c.SearchValuesRange("0060", "0070", false,
c.SearchValuesRange("0060", "0070", false, 0, func(n uint64) {},
func(id string, obj geojson.Object, fields []float64) bool {
if n > 0 {
expect(t, obj.String() > prevValue)
@ -353,7 +353,7 @@ func TestCollectionSearch(t *testing.T) {
expect(t, n == 10)
n = 0
c.SearchValuesRange("0070", "0060", true,
c.SearchValuesRange("0070", "0060", true, 0, func(n uint64) {},
func(id string, obj geojson.Object, fields []float64) bool {
if n > 0 {
expect(t, obj.String() < prevValue)
@ -436,7 +436,7 @@ func TestSpatialSearch(t *testing.T) {
var n int
n = 0
c.Within(q1, 0,
c.Within(q1, 0, 0, func(n uint64) {},
func(id string, obj geojson.Object, fields []float64) bool {
n++
return true
@ -445,7 +445,7 @@ func TestSpatialSearch(t *testing.T) {
expect(t, n == 3)
n = 0
c.Within(q2, 0,
c.Within(q2, 0, 0, func(n uint64) {},
func(id string, obj geojson.Object, fields []float64) bool {
n++
return true
@ -454,7 +454,7 @@ func TestSpatialSearch(t *testing.T) {
expect(t, n == 7)
n = 0
c.Within(q3, 0,
c.Within(q3, 0, 0, func(n uint64) {},
func(id string, obj geojson.Object, fields []float64) bool {
n++
return true
@ -463,7 +463,7 @@ func TestSpatialSearch(t *testing.T) {
expect(t, n == 4)
n = 0
c.Intersects(q1, 0,
c.Intersects(q1, 0, 0, func(n uint64) {},
func(_ string, _ geojson.Object, _ []float64) bool {
n++
return true
@ -472,7 +472,7 @@ func TestSpatialSearch(t *testing.T) {
expect(t, n == 4)
n = 0
c.Intersects(q2, 0,
c.Intersects(q2, 0, 0, func(n uint64) {},
func(_ string, _ geojson.Object, _ []float64) bool {
n++
return true
@ -481,7 +481,7 @@ func TestSpatialSearch(t *testing.T) {
expect(t, n == 7)
n = 0
c.Intersects(q3, 0,
c.Intersects(q3, 0, 0, func(n uint64) {},
func(_ string, _ geojson.Object, _ []float64) bool {
n++
return true
@ -490,7 +490,7 @@ func TestSpatialSearch(t *testing.T) {
expect(t, n == 5)
n = 0
c.Intersects(q3, 0,
c.Intersects(q3, 0, 0, func(n uint64) {},
func(_ string, _ geojson.Object, _ []float64) bool {
n++
return n <= 1
@ -502,7 +502,7 @@ func TestSpatialSearch(t *testing.T) {
exitems := []geojson.Object{
r2, p1, p4, r1, p3, r3, p2,
}
c.Nearby(q4,
c.Nearby(q4, 0, func(n uint64) {},
func(id string, obj geojson.Object, fields []float64) bool {
items = append(items, obj)
return true
@ -528,7 +528,7 @@ func TestCollectionSparse(t *testing.T) {
}
var n int
n = 0
c.Within(rect, 1,
c.Within(rect, 0,1, func(n uint64) {},
func(id string, obj geojson.Object, fields []float64) bool {
n++
return true
@ -537,7 +537,7 @@ func TestCollectionSparse(t *testing.T) {
expect(t, n == 4)
n = 0
c.Within(rect, 2,
c.Within(rect, 0, 2, func(n uint64) {},
func(id string, obj geojson.Object, fields []float64) bool {
n++
return true
@ -546,7 +546,7 @@ func TestCollectionSparse(t *testing.T) {
expect(t, n == 16)
n = 0
c.Within(rect, 3,
c.Within(rect, 0, 3, func(n uint64) {},
func(id string, obj geojson.Object, fields []float64) bool {
n++
return true
@ -555,7 +555,7 @@ func TestCollectionSparse(t *testing.T) {
expect(t, n == 64)
n = 0
c.Within(rect, 3,
c.Within(rect, 0, 3, func(n uint64) {},
func(id string, obj geojson.Object, fields []float64) bool {
n++
return n <= 30
@ -564,7 +564,7 @@ func TestCollectionSparse(t *testing.T) {
expect(t, n == 31)
n = 0
c.Intersects(rect, 3,
c.Intersects(rect, 0, 3, func(n uint64) {},
func(id string, _ geojson.Object, _ []float64) bool {
n++
return true
@ -573,7 +573,7 @@ func TestCollectionSparse(t *testing.T) {
expect(t, n == 64)
n = 0
c.Intersects(rect, 3,
c.Intersects(rect, 0, 3, func(n uint64) {},
func(id string, _ geojson.Object, _ []float64) bool {
n++
return n <= 30

View File

@ -372,9 +372,9 @@ func (server *Server) cmdPdel(msg *Message) (res resp.Value, d commandDetailsT,
if col != nil {
g := glob.Parse(d.pattern, false)
if g.Limits[0] == "" && g.Limits[1] == "" {
col.Scan(false, iter)
col.Scan(false, 0, func(n uint64) {}, iter)
} else {
col.ScanRange(g.Limits[0], g.Limits[1], false, iter)
col.ScanRange(g.Limits[0], g.Limits[1], false, 0, func(n uint64) {}, iter)
}
var atLeastOneNotDeleted bool
for i, dc := range d.children {

View File

@ -299,10 +299,10 @@ func extendRoamMessage(
}
g := glob.Parse(pattern, false)
if g.Limits[0] == "" && g.Limits[1] == "" {
col.Scan(false, iterator)
col.Scan(false, 0, func(n uint64) {}, iterator)
} else {
col.ScanRange(g.Limits[0], g.Limits[1],
false, iterator)
false, 0, func(n uint64) {}, iterator)
}
}
nmsg = append(nmsg, ']')
@ -364,7 +364,7 @@ func fenceMatchRoam(
prevNearbys := fence.roam.nearbys[tid]
var newNearbys map[string]bool
col.Intersects(obj, 0, func(
col.Intersects(obj, 0, 0, func(n uint64) {}, func(
id string, obj2 geojson.Object, fields []float64,
) bool {
if c.hasExpired(fence.roam.key, id) {

View File

@ -66,7 +66,7 @@ func (c *Server) cmdScan(msg *Message) (res resp.Value, err error) {
} else {
g := glob.Parse(sw.globPattern, s.desc)
if g.Limits[0] == "" && g.Limits[1] == "" {
sw.col.Scan(s.desc,
sw.col.Scan(s.desc, s.cursor, sw.IncCursor,
func(id string, o geojson.Object, fields []float64) bool {
return sw.writeObject(ScanWriterParams{
id: id,
@ -76,7 +76,7 @@ func (c *Server) cmdScan(msg *Message) (res resp.Value, err error) {
},
)
} else {
sw.col.ScanRange(g.Limits[0], g.Limits[1], s.desc,
sw.col.ScanRange(g.Limits[0], g.Limits[1], s.desc, s.cursor, sw.IncCursor,
func(id string, o geojson.Object, fields []float64) bool {
return sw.writeObject(ScanWriterParams{
id: id,

View File

@ -42,6 +42,7 @@ type scanWriter struct {
wheres []whereT
whereins []whereinT
whereevals []whereevalT
numberIters uint64
numberItems uint64
nofields bool
cursor uint64
@ -68,6 +69,7 @@ type ScanWriterParams struct {
noLock bool
ignoreGlobMatch bool
clip geojson.Object
skipTesting bool
}
func (c *Server) newScanWriter(
@ -165,7 +167,7 @@ func (sw *scanWriter) writeHead() {
func (sw *scanWriter) writeFoot() {
sw.mu.Lock()
defer sw.mu.Unlock()
cursor := sw.cursor + sw.numberItems
cursor := sw.numberIters
if !sw.hitLimit {
cursor = 0
}
@ -324,28 +326,43 @@ func (sw *scanWriter) globMatch(id string, o geojson.Object) (ok, keepGoing bool
return true, true
}
// Increment cursor
func (sw *scanWriter) IncCursor(n uint64) {
sw.numberIters += n
}
// ok is whether the object passes the test and should be written
// keepGoing is whether there could be more objects to test
func (sw *scanWriter) testObject(id string, o geojson.Object, fields []float64, ignoreGlobMatch bool) (
ok, keepGoing bool, fieldVals []float64) {
if !ignoreGlobMatch {
match, kg := sw.globMatch(id, o)
if !match {
return true, kg, fieldVals
}
}
nf, ok := sw.fieldMatch(fields, o)
return ok,true, nf
}
//id string, o geojson.Object, fields []float64, noLock bool
func (sw *scanWriter) writeObject(opts ScanWriterParams) bool {
if !opts.noLock {
sw.mu.Lock()
defer sw.mu.Unlock()
}
var fieldVals []float64
var ok bool
keepGoing := true
if !opts.ignoreGlobMatch {
var match bool
match, keepGoing = sw.globMatch(opts.id, opts.o)
if !match {
return true
if opts.skipTesting {
fieldVals = sw.fvals
} else {
ok, keepGoing, fieldVals = sw.testObject(opts.id, opts.o, opts.fields, opts.ignoreGlobMatch)
if !ok {
return keepGoing
}
}
nfields, ok := sw.fieldMatch(opts.fields, opts.o)
if !ok {
return true
}
sw.count++
if sw.count <= sw.cursor {
return true
}
if sw.output == outputCount {
return sw.count < sw.limit
}
@ -382,7 +399,7 @@ func (sw *scanWriter) writeObject(opts ScanWriterParams) bool {
} else if len(sw.farr) > 0 {
jsfields = `,"fields":[`
for i, field := range nfields {
for i, field := range fieldVals {
if i > 0 {
jsfields += ","
}

View File

@ -382,6 +382,7 @@ func (server *Server) cmdNearby(msg *Message) (res resp.Value, err error) {
distance: meters,
noLock: true,
ignoreGlobMatch: true,
skipTesting: true,
})
}
server.nearestNeighbors(&s, sw, s.obj.(*geojson.Circle), iter)
@ -405,18 +406,15 @@ func (server *Server) nearestNeighbors(
s *liveFenceSwitches, sw *scanWriter, target *geojson.Circle,
iter func(id string, o geojson.Object, fields []float64, dist float64,
) bool) {
limit := int(sw.cursor + sw.limit)
maxDist := target.Haversine()
limit := int(sw.limit)
var items []iterItem
sw.col.Nearby(target, func(id string, o geojson.Object, fields []float64) bool {
sw.col.Nearby(target, sw.cursor, sw.IncCursor, func(id string, o geojson.Object, fields []float64) bool {
if server.hasExpired(s.key, id) {
return true
}
if _, ok := sw.fieldMatch(fields, o); !ok {
return true
}
match, keepGoing := sw.globMatch(id, o)
if !match {
ok, keepGoing, _ := sw.testObject(id, o, fields,true)
if !ok {
return true
}
dist := target.HaversineTo(o.Center())
@ -482,7 +480,7 @@ func (server *Server) cmdWithinOrIntersects(cmd string, msg *Message) (res resp.
sw.writeHead()
if sw.col != nil {
if cmd == "within" {
sw.col.Within(s.obj, s.sparse, func(
sw.col.Within(s.obj, s.cursor, s.sparse, sw.IncCursor, func(
id string, o geojson.Object, fields []float64,
) bool {
if server.hasExpired(s.key, id) {
@ -496,7 +494,7 @@ func (server *Server) cmdWithinOrIntersects(cmd string, msg *Message) (res resp.
})
})
} else if cmd == "intersects" {
sw.col.Intersects(s.obj, s.sparse, func(
sw.col.Intersects(s.obj, s.cursor, s.sparse, sw.IncCursor, func(
id string,
o geojson.Object,
fields []float64,
@ -580,7 +578,7 @@ func (server *Server) cmdSearch(msg *Message) (res resp.Value, err error) {
} else {
g := glob.Parse(sw.globPattern, s.desc)
if g.Limits[0] == "" && g.Limits[1] == "" {
sw.col.SearchValues(s.desc,
sw.col.SearchValues(s.desc, s.cursor, sw.IncCursor,
func(id string, o geojson.Object, fields []float64) bool {
return sw.writeObject(ScanWriterParams{
id: id,
@ -594,7 +592,7 @@ func (server *Server) cmdSearch(msg *Message) (res resp.Value, err error) {
// must disable globSingle for string value type matching because
// globSingle is only for ID matches, not values.
sw.globSingle = false
sw.col.SearchValuesRange(g.Limits[0], g.Limits[1], s.desc,
sw.col.SearchValuesRange(g.Limits[0], g.Limits[1], s.desc, s.cursor, sw.IncCursor,
func(id string, o geojson.Object, fields []float64) bool {
return sw.writeObject(ScanWriterParams{
id: id,

View File

@ -6,10 +6,15 @@ import (
func subTestSearch(t *testing.T, mc *mockServer) {
runStep(t, mc, "KNN", keys_KNN_test)
runStep(t, mc, "KNN_CURSOR", keys_KNN_cursor_test)
runStep(t, mc, "WITHIN_CIRCLE", keys_WITHIN_CIRCLE_test)
runStep(t, mc, "INTERSECTS_CIRCLE", keys_INTERSECTS_CIRCLE_test)
runStep(t, mc, "WITHIN", keys_WITHIN_test)
runStep(t, mc, "WITHIN_CURSOR", keys_WITHIN_CURSOR_test)
runStep(t, mc, "INTERSECTS", keys_INTERSECTS_test)
runStep(t, mc, "INTERSECTS_CURSOR", keys_INTERSECTS_CURSOR_test)
runStep(t, mc, "SCAN_CURSOR", keys_SCAN_CURSOR_test)
runStep(t, mc, "SEARCH_CURSOR", keys_SEARCH_CURSOR_test)
}
func keys_KNN_test(mc *mockServer) error {
@ -26,6 +31,28 @@ func keys_KNN_test(mc *mockServer) error {
})
}
func keys_KNN_cursor_test(mc *mockServer) error {
return mc.DoBatch([][]interface{}{
{"SET", "mykey", "1", "FIELD", "foo", 5.5, "POINT", 5, 5}, {"OK"},
{"SET", "mykey", "2", "FIELD", "foo", 19.19, "POINT",19, 19}, {"OK"},
{"SET", "mykey", "3", "FIELD", "foo", 12.19, "POINT", 12, 19}, {"OK"},
{"SET", "mykey", "4", "FIELD", "foo", -5.5, "POINT", -5, 5}, {"OK"},
{"SET", "mykey", "5", "FIELD", "foo", 13.21, "POINT", 33, 21}, {"OK"},
{"NEARBY", "mykey", "LIMIT", 2, "POINTS", "POINT", 20, 20}, {
"[2 [[2 [19 19] [foo 19.19]] [3 [12 19] [foo 12.19]]]]"},
{"NEARBY", "mykey", "CURSOR", 2, "LIMIT", 1, "POINTS", "POINT", 20, 20}, {
"[3 [[5 [33 21] [foo 13.21]]]]"},
{"NEARBY", "mykey", "LIMIT", 2, "WHERE", "foo", -10, 15, "POINTS", "POINT", 20, 20}, {
"[3 [[3 [12 19] [foo 12.19]] [5 [33 21] [foo 13.21]]]]"},
{"NEARBY", "mykey", "CURSOR", 3, "LIMIT", 1, "WHERE", "foo", -10, 15, "POINTS", "POINT", 20, 20}, {
"[4 [[1 [5 5] [foo 5.5]]]]"},
{"NEARBY", "mykey", "CURSOR", 4, "LIMIT", 1, "WHERE", "foo", -10, 15, "POINTS", "POINT", 20, 20}, {
"[5 [[4 [-5 5] [foo -5.5]]]]"},
{"NEARBY", "mykey", "CURSOR", 4, "LIMIT", 10, "WHERE", "foo", -10, 15, "POINTS", "POINT", 20, 20}, {
"[0 [[4 [-5 5] [foo -5.5]]]]"},
})
}
func keys_WITHIN_test(mc *mockServer) error {
return mc.DoBatch([][]interface{}{
{"SET", "mykey", "point1", "POINT", 37.7335, -122.4412}, {"OK"},
@ -73,6 +100,44 @@ func keys_WITHIN_test(mc *mockServer) error {
})
}
func keys_WITHIN_CURSOR_test(mc *mockServer) error {
testArea := `{
"type": "Polygon",
"coordinates": [
[
[-122.44126439094543,37.72906137107],
[-122.43980526924135,37.72906137107],
[-122.43980526924135,37.73421283683962],
[-122.44126439094543,37.73421283683962],
[-122.44126439094543,37.72906137107]
]
]
}`
return mc.DoBatch([][]interface{}{
{"SET", "mykey", "point1", "FIELD", "foo", 1, "POINT", 37.7335, -122.4412}, {"OK"},
{"SET", "mykey", "point2", "FIELD", "foo", 2, "POINT", 37.7335, -122.44121}, {"OK"},
{"SET", "mykey", "line3", "FIELD", "foo", 3, "OBJECT", `{"type":"LineString","coordinates":[[-122.4408378,37.7341129],[-122.4408378,37.733]]}`}, {"OK"},
{"SET", "mykey", "poly4", "FIELD", "foo", 4, "OBJECT", `{"type":"Polygon","coordinates":[[[-122.4408378,37.7341129],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.7341129],[-122.4408378,37.7341129]]]}`}, {"OK"},
{"SET", "mykey", "multipoly5","FIELD", "foo", 5, "OBJECT", `{"type":"MultiPolygon","coordinates":[[[[-122.4408378,37.7341129],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.7341129],[-122.4408378,37.7341129]]],[[[-122.44091033935547,37.731981251280985],[-122.43994474411011,37.731981251280985],[-122.43994474411011,37.73254976045042],[-122.44091033935547,37.73254976045042],[-122.44091033935547,37.731981251280985]]]]}`}, {"OK"},
{"SET", "mykey", "point6", "FIELD", "foo", 6, "POINT", -5, 5}, {"OK"},
{"SET", "mykey", "point7", "FIELD", "foo", 7, "POINT", 33, 21}, {"OK"},
{"SET", "mykey", "poly8", "FIELD", "foo", 8, "OBJECT", `{"type":"Polygon","coordinates":[[[-122.4408378,37.7341129],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.7341129],[-122.4408378,37.7341129]],[[-122.44060993194579,37.73345766902749],[-122.44044363498686,37.73345766902749],[-122.44044363498686,37.73355524732416],[-122.44060993194579,37.73355524732416],[-122.44060993194579,37.73345766902749]],[[-122.44060724973677,37.7336888869566],[-122.4402102828026,37.7336888869566],[-122.4402102828026,37.7339752567853],[-122.44060724973677,37.7339752567853],[-122.44060724973677,37.7336888869566]]]}`}, {"OK"},
{"SET", "mykey", "point9", "FIELD", "foo", 9, "POINT", 37.7335, -122.4412}, {"OK"},
{"WITHIN", "mykey", "LIMIT", 3, "IDS", "OBJECT", testArea}, {
"[3 [point1 point2 line3]]"},
{"WITHIN", "mykey", "CURSOR", 3, "LIMIT", 3, "IDS", "OBJECT", testArea}, {
"[6 [poly4 multipoly5 poly8]]"},
{"WITHIN", "mykey", "WHERE", "foo", 3, 5, "IDS", "OBJECT", testArea}, {
"[0 [line3 poly4 multipoly5]]"},
{"WITHIN", "mykey", "LIMIT", 1, "WHERE", "foo", 3, 5, "IDS", "OBJECT", testArea}, {
"[3 [line3]]"},
{"WITHIN", "mykey", "CURSOR", 3, "LIMIT", 1, "WHERE", "foo", 8, 9, "IDS", "OBJECT", testArea}, {
"[6 [poly8]]"},
{"WITHIN", "mykey", "CURSOR", 6, "LIMIT", 1, "WHERE", "foo", 8, 9, "IDS", "OBJECT", testArea}, {
"[7 [point9]]"},
})
}
func keys_INTERSECTS_test(mc *mockServer) error {
return mc.DoBatch([][]interface{}{
{"SET", "mykey", "point1", "POINT", 37.7335, -122.4412}, {"OK"},
@ -123,6 +188,44 @@ func keys_INTERSECTS_test(mc *mockServer) error {
})
}
func keys_INTERSECTS_CURSOR_test(mc *mockServer) error {
testArea := `{
"type": "Polygon",
"coordinates": [
[
[-122.44126439094543,37.732906137107],
[-122.43980526924135,37.732906137107],
[-122.43980526924135,37.73421283683962],
[-122.44126439094543,37.73421283683962],
[-122.44126439094543,37.732906137107]
]
]
}`
return mc.DoBatch([][]interface{}{
{"SET", "mykey", "point1", "FIELD", "foo", 1, "POINT", 37.7335, -122.4412}, {"OK"},
{"SET", "mykey", "point2", "FIELD", "foo", 2, "POINT", 37.7335, -122.44121}, {"OK"},
{"SET", "mykey", "line3", "FIELD", "foo", 3, "OBJECT", `{"type":"LineString","coordinates":[[-122.4408378,37.7341129],[-122.4408378,37.733]]}`}, {"OK"},
{"SET", "mykey", "poly4", "FIELD", "foo", 4, "OBJECT", `{"type":"Polygon","coordinates":[[[-122.4408378,37.7341129],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.7341129],[-122.4408378,37.7341129]]]}`}, {"OK"},
{"SET", "mykey", "multipoly5", "FIELD", "foo", 5, "OBJECT", `{"type":"MultiPolygon","coordinates":[[[[-122.4408378,37.7341129],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.7341129],[-122.4408378,37.7341129]]],[[[-122.44091033935547,37.731981251280985],[-122.43994474411011,37.731981251280985],[-122.43994474411011,37.73254976045042],[-122.44091033935547,37.73254976045042],[-122.44091033935547,37.731981251280985]]]]}`}, {"OK"},
{"SET", "mykey", "point6", "FIELD", "foo", 6, "POINT", -5, 5}, {"OK"},
{"SET", "mykey", "point7", "FIELD", "foo", 7, "POINT", 33, 21}, {"OK"},
{"SET", "mykey", "poly8", "FIELD", "foo", 8, "OBJECT", `{"type":"Polygon","coordinates":[[[-122.4408378,37.7341129],[-122.4408378,37.733],[-122.44,37.733],[-122.44,37.7341129],[-122.4408378,37.7341129]],[[-122.44060993194579,37.73345766902749],[-122.44044363498686,37.73345766902749],[-122.44044363498686,37.73355524732416],[-122.44060993194579,37.73355524732416],[-122.44060993194579,37.73345766902749]],[[-122.44060724973677,37.7336888869566],[-122.4402102828026,37.7336888869566],[-122.4402102828026,37.7339752567853],[-122.44060724973677,37.7339752567853],[-122.44060724973677,37.7336888869566]]]}`}, {"OK"},
{"SET", "mykey", "point9", "FIELD", "foo", 9, "POINT", 37.7335, -122.4412}, {"OK"},
{"INTERSECTS", "mykey", "LIMIT", 3, "IDS", "OBJECT", testArea}, {
"[3 [point1 point2 line3]]"},
{"INTERSECTS", "mykey", "CURSOR", 3, "LIMIT", 3, "IDS", "OBJECT", testArea}, {
"[6 [poly4 multipoly5 poly8]]"},
{"INTERSECTS", "mykey", "WHERE", "foo", 3, 5, "IDS", "OBJECT", testArea}, {
"[0 [line3 poly4 multipoly5]]"},
{"INTERSECTS", "mykey", "LIMIT", 1, "WHERE", "foo", 3, 5, "IDS", "OBJECT", testArea}, {
"[3 [line3]]"},
{"INTERSECTS", "mykey", "CURSOR", 3, "LIMIT", 1, "WHERE", "foo", 8, 9, "IDS", "OBJECT", testArea}, {
"[6 [poly8]]"},
{"INTERSECTS", "mykey", "CURSOR", 6, "LIMIT", 1, "WHERE", "foo", 8, 9, "IDS", "OBJECT", testArea}, {
"[7 [point9]]"},
})
}
func keys_WITHIN_CIRCLE_test(mc *mockServer) error {
return mc.DoBatch([][]interface{}{
{"SET", "mykey", "1", "POINT", 37.7335, -122.4412}, {"OK"},
@ -154,3 +257,48 @@ func keys_INTERSECTS_CIRCLE_test(mc *mockServer) error {
"[0 [1 2]]"},
})
}
func keys_SCAN_CURSOR_test(mc *mockServer) error {
return mc.DoBatch([][]interface{}{
{"SET", "mykey", "id1", "FIELD", "foo", 1, "STRING", "bar1"}, {"OK"},
{"SET", "mykey", "id2", "FIELD", "foo", 2, "STRING", "bar2"}, {"OK"},
{"SET", "mykey", "id3", "FIELD", "foo", 3, "STRING", "bar3"}, {"OK"},
{"SET", "mykey", "id4", "FIELD", "foo", 4, "STRING", "bar4"}, {"OK"},
{"SET", "mykey", "id5", "FIELD", "foo", 5, "STRING", "bar5"}, {"OK"},
{"SET", "mykey", "id6", "FIELD", "foo", 6, "STRING", "bar6"}, {"OK"},
{"SET", "mykey", "id7", "FIELD", "foo", 7, "STRING", "bar7"}, {"OK"},
{"SET", "mykey", "id8", "FIELD", "foo", 8, "STRING", "bar8"}, {"OK"},
{"SET", "mykey", "id9", "FIELD", "foo", 9, "STRING", "bar9"}, {"OK"},
{"SCAN", "mykey", "LIMIT", 3, "IDS"}, {"[3 [id1 id2 id3]]"},
{"SCAN", "mykey", "CURSOR", 3, "LIMIT", 3, "IDS"}, {"[6 [id4 id5 id6]]"},
{"SCAN", "mykey", "WHERE", "foo", 3, 5, "IDS"}, {"[0 [id3 id4 id5]]"},
{"SCAN", "mykey", "LIMIT", 1, "WHERE", "foo", 3, 5, "IDS"}, {"[3 [id3]]"},
{"SCAN", "mykey", "CURSOR", 3, "LIMIT", 1, "WHERE", "foo", 8, 9, "IDS"}, {
"[8 [id8]]"},
{"SCAN", "mykey", "CURSOR", 6, "LIMIT", 1, "WHERE", "foo", 8, 9, "IDS"}, {
"[8 [id8]]"},
})
}
func keys_SEARCH_CURSOR_test(mc *mockServer) error {
return mc.DoBatch([][]interface{}{
{"SET", "mykey", "id1", "FIELD", "foo", 1, "STRING", "bar1"}, {"OK"},
{"SET", "mykey", "id2", "FIELD", "foo", 2, "STRING", "bar2"}, {"OK"},
{"SET", "mykey", "id3", "FIELD", "foo", 3, "STRING", "bar3"}, {"OK"},
{"SET", "mykey", "id4", "FIELD", "foo", 4, "STRING", "bar4"}, {"OK"},
{"SET", "mykey", "id5", "FIELD", "foo", 5, "STRING", "bar5"}, {"OK"},
{"SET", "mykey", "id6", "FIELD", "foo", 6, "STRING", "bar6"}, {"OK"},
{"SET", "mykey", "id7", "FIELD", "foo", 7, "STRING", "bar7"}, {"OK"},
{"SET", "mykey", "id8", "FIELD", "foo", 8, "STRING", "bar8"}, {"OK"},
{"SET", "mykey", "id9", "FIELD", "foo", 9, "STRING", "bar9"}, {"OK"},
{"SEARCH", "mykey", "LIMIT", 3, "IDS"}, {"[3 [id1 id2 id3]]"},
{"SEARCH", "mykey", "CURSOR", 3, "LIMIT", 3, "IDS"}, {"[6 [id4 id5 id6]]"},
{"SEARCH", "mykey", "WHERE", "foo", 3, 5, "IDS"}, {"[0 [id3 id4 id5]]"},
{"SEARCH", "mykey", "LIMIT", 1, "WHERE", "foo", 3, 5, "IDS"}, {"[3 [id3]]"},
{"SEARCH", "mykey", "CURSOR", 3, "LIMIT", 1, "WHERE", "foo", 8, 9, "IDS"}, {
"[8 [id8]]"},
{"SEARCH", "mykey", "CURSOR", 6, "LIMIT", 1, "WHERE", "foo", 8, 9, "IDS"}, {
"[8 [id8]]"},
{"SEARCH", "mykey", "LIMIT", 3, "DESC", "IDS"}, {"[3 [id9 id8 id7]]"},
})
}