github.com/tidwall/tile38@v0.0.0-20230521152930-0144ca6883f3/internal/collection/collection_test.go (about)

     1  package collection
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"reflect"
     7  	"strconv"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/tidwall/geojson"
    12  	"github.com/tidwall/geojson/geometry"
    13  	"github.com/tidwall/gjson"
    14  	"github.com/tidwall/tile38/internal/field"
    15  	"github.com/tidwall/tile38/internal/object"
    16  )
    17  
    18  func PO(x, y float64) *geojson.Point {
    19  	return geojson.NewPoint(geometry.Point{X: x, Y: y})
    20  }
    21  
    22  func init() {
    23  	seed := time.Now().UnixNano()
    24  	println(seed)
    25  	rand.Seed(seed)
    26  }
    27  
    28  func expect(t testing.TB, expect bool) {
    29  	t.Helper()
    30  	if !expect {
    31  		t.Fatal("not what you expected")
    32  	}
    33  }
    34  
    35  func bounds(c *Collection) geometry.Rect {
    36  	minX, minY, maxX, maxY := c.Bounds()
    37  	return geometry.Rect{
    38  		Min: geometry.Point{X: minX, Y: minY},
    39  		Max: geometry.Point{X: maxX, Y: maxY},
    40  	}
    41  }
    42  
    43  func TestCollectionNewCollection(t *testing.T) {
    44  	const numItems = 10000
    45  	objs := make(map[string]geojson.Object)
    46  	c := New()
    47  	for i := 0; i < numItems; i++ {
    48  		id := strconv.FormatInt(int64(i), 10)
    49  		obj := PO(rand.Float64()*360-180, rand.Float64()*180-90)
    50  		objs[id] = obj
    51  		c.Set(object.New(id, obj, 0, field.List{}))
    52  	}
    53  	count := 0
    54  	bbox := geometry.Rect{
    55  		Min: geometry.Point{X: -180, Y: -90},
    56  		Max: geometry.Point{X: 180, Y: 90},
    57  	}
    58  	c.geoSearch(bbox, func(o *object.Object) bool {
    59  		count++
    60  		return true
    61  	})
    62  	if count != len(objs) {
    63  		t.Fatalf("count = %d, expect %d", count, len(objs))
    64  	}
    65  	count = c.Count()
    66  	if count != len(objs) {
    67  		t.Fatalf("c.Count() = %d, expect %d", count, len(objs))
    68  	}
    69  	testCollectionVerifyContents(t, c, objs)
    70  }
    71  
    72  func toFields(fNames, fValues []string) field.List {
    73  	var fields field.List
    74  	for i := 0; i < len(fNames); i++ {
    75  		fields = fields.Set(field.Make(fNames[i], fValues[i]))
    76  	}
    77  	return fields
    78  }
    79  
    80  func TestCollectionSet(t *testing.T) {
    81  	t.Run("AddString", func(t *testing.T) {
    82  		c := New()
    83  		str1 := String("hello")
    84  		old := c.Set(object.New("str", str1, 0, field.List{}))
    85  		expect(t, old == nil)
    86  	})
    87  	t.Run("UpdateString", func(t *testing.T) {
    88  		c := New()
    89  		str1 := String("hello")
    90  		str2 := String("world")
    91  		old := c.Set(object.New("str", str1, 0, field.List{}))
    92  		expect(t, old == nil)
    93  		old = c.Set(object.New("str", str2, 0, field.List{}))
    94  		expect(t, old.Geo() == str1)
    95  	})
    96  	t.Run("AddPoint", func(t *testing.T) {
    97  		c := New()
    98  		point1 := PO(-112.1, 33.1)
    99  		old := c.Set(object.New("point", point1, 0, field.List{}))
   100  		expect(t, old == nil)
   101  	})
   102  	t.Run("UpdatePoint", func(t *testing.T) {
   103  		c := New()
   104  		point1 := PO(-112.1, 33.1)
   105  		point2 := PO(-112.2, 33.2)
   106  		old := c.Set(object.New("point", point1, 0, field.List{}))
   107  		expect(t, old == nil)
   108  		old = c.Set(object.New("point", point2, 0, field.List{}))
   109  		expect(t, old.Geo().Center() == point1.Base())
   110  	})
   111  	t.Run("Fields", func(t *testing.T) {
   112  		c := New()
   113  		str1 := String("hello")
   114  
   115  		fNames := []string{"a", "b", "c"}
   116  		fValues := []string{"1", "2", "3"}
   117  		fields1 := toFields(fNames, fValues)
   118  		old := c.Set(object.New("str", str1, 0, fields1))
   119  		expect(t, old == nil)
   120  
   121  		str2 := String("hello")
   122  
   123  		fNames = []string{"d", "e", "f"}
   124  		fValues = []string{"4", "5", "6"}
   125  		fields2 := toFields(fNames, fValues)
   126  
   127  		old = c.Set(object.New("str", str2, 0, fields2))
   128  		expect(t, old.Geo() == str1)
   129  		expect(t, reflect.DeepEqual(old.Fields(), fields1))
   130  
   131  		fNames = []string{"a", "b", "c", "d", "e", "f"}
   132  		fValues = []string{"7", "8", "9", "10", "11", "12"}
   133  		fields3 := toFields(fNames, fValues)
   134  		old = c.Set(object.New("str", str1, 0, fields3))
   135  		expect(t, old.Geo() == str2)
   136  		expect(t, reflect.DeepEqual(old.Fields(), fields2))
   137  	})
   138  	t.Run("Delete", func(t *testing.T) {
   139  		c := New()
   140  
   141  		c.Set(object.New("1", String("1"), 0, field.List{}))
   142  		c.Set(object.New("2", String("2"), 0, field.List{}))
   143  		c.Set(object.New("3", PO(1, 2), 0, field.List{}))
   144  
   145  		expect(t, c.Count() == 3)
   146  		expect(t, c.StringCount() == 2)
   147  		expect(t, c.PointCount() == 1)
   148  		expect(t, bounds(c) == geometry.Rect{
   149  			Min: geometry.Point{X: 1, Y: 2},
   150  			Max: geometry.Point{X: 1, Y: 2}})
   151  		var prev *object.Object
   152  
   153  		prev = c.Delete("2")
   154  		expect(t, prev.Geo().String() == "2")
   155  		expect(t, c.Count() == 2)
   156  		expect(t, c.StringCount() == 1)
   157  		expect(t, c.PointCount() == 1)
   158  
   159  		prev = c.Delete("1")
   160  		expect(t, prev.Geo().String() == "1")
   161  		expect(t, c.Count() == 1)
   162  		expect(t, c.StringCount() == 0)
   163  		expect(t, c.PointCount() == 1)
   164  
   165  		prev = c.Delete("3")
   166  		expect(t, prev.Geo().String() == `{"type":"Point","coordinates":[1,2]}`)
   167  		expect(t, c.Count() == 0)
   168  		expect(t, c.StringCount() == 0)
   169  		expect(t, c.PointCount() == 0)
   170  		prev = c.Delete("3")
   171  		expect(t, prev == nil)
   172  		expect(t, c.Count() == 0)
   173  		expect(t, bounds(c) == geometry.Rect{})
   174  		expect(t, c.Get("3") == nil)
   175  	})
   176  }
   177  
   178  func fieldValueAt(fields field.List, index int) string {
   179  	if index < 0 || index >= fields.Len() {
   180  		panic("out of bounds")
   181  	}
   182  	var retval string
   183  	var i int
   184  	fields.Scan(func(f field.Field) bool {
   185  		if i == index {
   186  			retval = f.Value().Data()
   187  		}
   188  		i++
   189  		return true
   190  	})
   191  	return retval
   192  }
   193  
   194  func TestCollectionScan(t *testing.T) {
   195  	N := 256
   196  	c := New()
   197  	for _, i := range rand.Perm(N) {
   198  		id := fmt.Sprintf("%04d", i)
   199  		c.Set(object.New(id, String(id), 0, makeFields(
   200  			field.Make("ex", id),
   201  		)))
   202  	}
   203  	var n int
   204  	var prevID string
   205  	c.Scan(false, nil, nil, func(o *object.Object) bool {
   206  		if n > 0 {
   207  			expect(t, o.ID() > prevID)
   208  		}
   209  		expect(t, o.ID() == fieldValueAt(o.Fields(), 0))
   210  		n++
   211  		prevID = o.ID()
   212  		return true
   213  	})
   214  	expect(t, n == c.Count())
   215  	n = 0
   216  	c.Scan(true, nil, nil, func(o *object.Object) bool {
   217  		if n > 0 {
   218  			expect(t, o.ID() < prevID)
   219  		}
   220  		expect(t, o.ID() == fieldValueAt(o.Fields(), 0))
   221  		n++
   222  		prevID = o.ID()
   223  		return true
   224  	})
   225  	expect(t, n == c.Count())
   226  
   227  	n = 0
   228  	c.ScanRange("0060", "0070", false, nil, nil,
   229  		func(o *object.Object) bool {
   230  			if n > 0 {
   231  				expect(t, o.ID() > prevID)
   232  			}
   233  			expect(t, o.ID() == fieldValueAt(o.Fields(), 0))
   234  			n++
   235  			prevID = o.ID()
   236  			return true
   237  		})
   238  	expect(t, n == 10)
   239  
   240  	n = 0
   241  	c.ScanRange("0070", "0060", true, nil, nil,
   242  		func(o *object.Object) bool {
   243  			if n > 0 {
   244  				expect(t, o.ID() < prevID)
   245  			}
   246  			expect(t, o.ID() == fieldValueAt(o.Fields(), 0))
   247  			n++
   248  			prevID = o.ID()
   249  			return true
   250  		})
   251  	expect(t, n == 10)
   252  
   253  	n = 0
   254  	c.ScanGreaterOrEqual("0070", true, nil, nil,
   255  		func(o *object.Object) bool {
   256  			if n > 0 {
   257  				expect(t, o.ID() < prevID)
   258  			}
   259  			expect(t, o.ID() == fieldValueAt(o.Fields(), 0))
   260  			n++
   261  			prevID = o.ID()
   262  			return true
   263  		})
   264  	expect(t, n == 71)
   265  
   266  	n = 0
   267  	c.ScanGreaterOrEqual("0070", false, nil, nil,
   268  		func(o *object.Object) bool {
   269  			if n > 0 {
   270  				expect(t, o.ID() > prevID)
   271  			}
   272  			expect(t, o.ID() == fieldValueAt(o.Fields(), 0))
   273  			n++
   274  			prevID = o.ID()
   275  			return true
   276  		})
   277  	expect(t, n == c.Count()-70)
   278  
   279  }
   280  
   281  func makeFields(entries ...field.Field) field.List {
   282  	var fields field.List
   283  	for _, f := range entries {
   284  		fields = fields.Set(f)
   285  	}
   286  	return fields
   287  }
   288  
   289  func TestCollectionSearch(t *testing.T) {
   290  	N := 256
   291  	c := New()
   292  	for i, j := range rand.Perm(N) {
   293  		id := fmt.Sprintf("%04d", j)
   294  		ex := fmt.Sprintf("%04d", i)
   295  		c.Set(object.New(id, String(ex),
   296  			0, makeFields(
   297  				field.Make("i", ex),
   298  				field.Make("j", id),
   299  			)))
   300  	}
   301  	var n int
   302  	var prevValue string
   303  	c.SearchValues(false, nil, nil, func(o *object.Object) bool {
   304  		if n > 0 {
   305  			expect(t, o.Geo().String() > prevValue)
   306  		}
   307  		expect(t, o.ID() == fieldValueAt(o.Fields(), 1))
   308  		n++
   309  		prevValue = o.Geo().String()
   310  		return true
   311  	})
   312  	expect(t, n == c.Count())
   313  	n = 0
   314  	c.SearchValues(true, nil, nil, func(o *object.Object) bool {
   315  		if n > 0 {
   316  			expect(t, o.Geo().String() < prevValue)
   317  		}
   318  		expect(t, o.ID() == fieldValueAt(o.Fields(), 1))
   319  		n++
   320  		prevValue = o.Geo().String()
   321  		return true
   322  	})
   323  	expect(t, n == c.Count())
   324  
   325  	n = 0
   326  	c.SearchValuesRange("0060", "0070", false, nil, nil,
   327  		func(o *object.Object) bool {
   328  			if n > 0 {
   329  				expect(t, o.Geo().String() > prevValue)
   330  			}
   331  			expect(t, o.ID() == fieldValueAt(o.Fields(), 1))
   332  			n++
   333  			prevValue = o.Geo().String()
   334  			return true
   335  		})
   336  	expect(t, n == 10)
   337  
   338  	n = 0
   339  	c.SearchValuesRange("0070", "0060", true, nil, nil,
   340  		func(o *object.Object) bool {
   341  			if n > 0 {
   342  				expect(t, o.Geo().String() < prevValue)
   343  			}
   344  			expect(t, o.ID() == fieldValueAt(o.Fields(), 1))
   345  			n++
   346  			prevValue = o.Geo().String()
   347  			return true
   348  		})
   349  	expect(t, n == 10)
   350  }
   351  
   352  func TestCollectionWeight(t *testing.T) {
   353  	c := New()
   354  	c.Set(object.New("1", String("1"), 0, field.List{}))
   355  	expect(t, c.TotalWeight() > 0)
   356  	c.Delete("1")
   357  	expect(t, c.TotalWeight() == 0)
   358  	c.Set(object.New("1", String("1"), 0,
   359  		toFields(
   360  			[]string{"a", "b", "c"},
   361  			[]string{"1", "2", "3"},
   362  		),
   363  	))
   364  	expect(t, c.TotalWeight() > 0)
   365  	c.Delete("1")
   366  	expect(t, c.TotalWeight() == 0)
   367  	c.Set(object.New("1", String("1"), 0,
   368  		toFields(
   369  			[]string{"a", "b", "c"},
   370  			[]string{"1", "2", "3"},
   371  		),
   372  	))
   373  	c.Set(object.New("2", String("2"), 0,
   374  		toFields(
   375  			[]string{"d", "e", "f"},
   376  			[]string{"4", "5", "6"},
   377  		),
   378  	))
   379  	c.Set(object.New("1", String("1"), 0,
   380  		toFields(
   381  			[]string{"d", "e", "f"},
   382  			[]string{"4", "5", "6"},
   383  		),
   384  	))
   385  	c.Delete("1")
   386  	c.Delete("2")
   387  	expect(t, c.TotalWeight() == 0)
   388  }
   389  
   390  func TestSpatialSearch(t *testing.T) {
   391  	json := `
   392  		{"type":"FeatureCollection","features":[
   393  			{"type":"Feature","id":"p1","properties":{"marker-color":"#962d28","stroke":"#962d28","fill":"#962d28"},"geometry":{"type":"Point","coordinates":[-71.4743041992187,42.51867517417283]}},
   394  			{"type":"Feature","id":"p2","properties":{"marker-color":"#962d28","stroke":"#962d28","fill":"#962d28"},"geometry":{"type":"Point","coordinates":[-71.4056396484375,42.50197174319114]}},
   395  			{"type":"Feature","id":"p3","properties":{"marker-color":"#962d28","stroke":"#962d28","fill":"#962d28"},"geometry":{"type":"Point","coordinates":[-71.4619445800781,42.49437779897246]}},
   396  			{"type":"Feature","id":"p4","properties":{"marker-color":"#962d28","stroke":"#962d28","fill":"#962d28"},"geometry":{"type":"Point","coordinates":[-71.4337921142578,42.53891577257117]}},
   397  			{"type":"Feature","id":"r1","properties":{"marker-color":"#962d28","stroke":"#962d28","fill":"#962d28"},"geometry":{"type":"Polygon","coordinates":[[[-71.4279556274414,42.48804880765346],[-71.37439727783203,42.48804880765346],[-71.37439727783203,42.52322988064187],[-71.4279556274414,42.52322988064187],[-71.4279556274414,42.48804880765346]]]}},
   398  			{"type":"Feature","id":"r2","properties":{"marker-color":"#962d28","stroke":"#962d28","fill":"#962d28"},"geometry":{"type":"Polygon","coordinates":[[[-71.4825439453125,42.53588010092859],[-71.45027160644531,42.53588010092859],[-71.45027160644531,42.55839115400447],[-71.4825439453125,42.55839115400447],[-71.4825439453125,42.53588010092859]]]}},
   399  			{"type":"Feature","id":"r3","properties":{"marker-color":"#962d28","stroke":"#962d28","fill":"#962d28"},"geometry":{"type":"Polygon","coordinates": [[[-71.4111328125,42.53512115995963],[-71.3833236694336,42.53512115995963],[-71.3833236694336,42.54953946116446],[-71.4111328125,42.54953946116446],[-71.4111328125,42.53512115995963]]]}},
   400  			{"type":"Feature","id":"q1","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-71.55258178710938,42.51361399979923],[-71.42074584960938,42.51361399979923],[-71.42074584960938,42.59100512331456],[-71.55258178710938,42.59100512331456],[-71.55258178710938,42.51361399979923]]]}},
   401  			{"type":"Feature","id":"q2","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-71.52992248535156,42.48121277771616],[-71.36375427246092,42.48121277771616],[-71.36375427246092,42.57786045892046],[-71.52992248535156,42.57786045892046],[-71.52992248535156,42.48121277771616]]]}},
   402  			{"type":"Feature","id":"q3","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-71.49490356445312,42.56673588590953],[-71.52236938476562,42.47462922809497],[-71.42898559570312,42.464499337722344],[-71.43241882324219,42.522217752342236],[-71.37954711914061,42.56420729713456],[-71.49490356445312,42.56673588590953]]]}},
   403  			{"type":"Feature","id":"q4","properties":{},"geometry":{"type":"Point","coordinates": [-71.46366119384766,42.54043355305221]}}
   404  		]}
   405  	`
   406  	p1, _ := geojson.Parse(gjson.Get(json, `features.#[id=="p1"]`).Raw, nil)
   407  	p2, _ := geojson.Parse(gjson.Get(json, `features.#[id=="p2"]`).Raw, nil)
   408  	p3, _ := geojson.Parse(gjson.Get(json, `features.#[id=="p3"]`).Raw, nil)
   409  	p4, _ := geojson.Parse(gjson.Get(json, `features.#[id=="p4"]`).Raw, nil)
   410  	r1, _ := geojson.Parse(gjson.Get(json, `features.#[id=="r1"]`).Raw, nil)
   411  	r2, _ := geojson.Parse(gjson.Get(json, `features.#[id=="r2"]`).Raw, nil)
   412  	r3, _ := geojson.Parse(gjson.Get(json, `features.#[id=="r3"]`).Raw, nil)
   413  	q1, _ := geojson.Parse(gjson.Get(json, `features.#[id=="q1"]`).Raw, nil)
   414  	q2, _ := geojson.Parse(gjson.Get(json, `features.#[id=="q2"]`).Raw, nil)
   415  	q3, _ := geojson.Parse(gjson.Get(json, `features.#[id=="q3"]`).Raw, nil)
   416  	q4, _ := geojson.Parse(gjson.Get(json, `features.#[id=="q4"]`).Raw, nil)
   417  
   418  	c := New()
   419  	c.Set(object.New("p1", p1, 0, field.List{}))
   420  	c.Set(object.New("p2", p2, 0, field.List{}))
   421  	c.Set(object.New("p3", p3, 0, field.List{}))
   422  	c.Set(object.New("p4", p4, 0, field.List{}))
   423  	c.Set(object.New("r1", r1, 0, field.List{}))
   424  	c.Set(object.New("r2", r2, 0, field.List{}))
   425  	c.Set(object.New("r3", r3, 0, field.List{}))
   426  
   427  	var n int
   428  
   429  	n = 0
   430  	c.Within(q1, 0, nil, nil, func(o *object.Object) bool {
   431  		n++
   432  		return true
   433  	})
   434  	expect(t, n == 3)
   435  
   436  	n = 0
   437  	c.Within(q2, 0, nil, nil, func(o *object.Object) bool {
   438  		n++
   439  		return true
   440  	})
   441  	expect(t, n == 7)
   442  
   443  	n = 0
   444  	c.Within(q3, 0, nil, nil, func(o *object.Object) bool {
   445  		n++
   446  		return true
   447  	})
   448  	expect(t, n == 4)
   449  
   450  	n = 0
   451  	c.Intersects(q1, 0, nil, nil, func(o *object.Object) bool {
   452  		n++
   453  		return true
   454  	})
   455  	expect(t, n == 4)
   456  
   457  	n = 0
   458  	c.Intersects(q2, 0, nil, nil, func(o *object.Object) bool {
   459  		n++
   460  		return true
   461  	})
   462  	expect(t, n == 7)
   463  
   464  	n = 0
   465  	c.Intersects(q3, 0, nil, nil, func(o *object.Object) bool {
   466  		n++
   467  		return true
   468  	})
   469  	expect(t, n == 5)
   470  
   471  	n = 0
   472  	c.Intersects(q3, 0, nil, nil, func(o *object.Object) bool {
   473  		n++
   474  		return n <= 1
   475  	})
   476  	expect(t, n == 2)
   477  
   478  	var items []geojson.Object
   479  	exitems := []geojson.Object{
   480  		r2, p4, p1, r1, r3, p3, p2,
   481  	}
   482  
   483  	lastDist := float64(-1)
   484  	distsMonotonic := true
   485  	c.Nearby(q4, nil, nil, func(o *object.Object, dist float64) bool {
   486  		if dist < lastDist {
   487  			distsMonotonic = false
   488  		}
   489  		items = append(items, o.Geo())
   490  		return true
   491  	})
   492  	expect(t, len(items) == 7)
   493  	expect(t, distsMonotonic)
   494  	expect(t, reflect.DeepEqual(items, exitems))
   495  }
   496  
   497  func TestCollectionSparse(t *testing.T) {
   498  	rect := geojson.NewRect(geometry.Rect{
   499  		Min: geometry.Point{X: -71.598930, Y: 42.4586739},
   500  		Max: geometry.Point{X: -71.37302, Y: 42.607937},
   501  	})
   502  	N := 10000
   503  	c := New()
   504  	r := rect.Rect()
   505  	for i := 0; i < N; i++ {
   506  		x := (r.Max.X-r.Min.X)*rand.Float64() + r.Min.X
   507  		y := (r.Max.Y-r.Min.Y)*rand.Float64() + r.Min.Y
   508  		point := PO(x, y)
   509  		c.Set(object.New(fmt.Sprintf("%d", i), point, 0, field.List{}))
   510  	}
   511  	var n int
   512  	n = 0
   513  	c.Within(rect, 1, nil, nil, func(o *object.Object) bool {
   514  		n++
   515  		return true
   516  	})
   517  	expect(t, n == 4)
   518  
   519  	n = 0
   520  	c.Within(rect, 2, nil, nil, func(o *object.Object) bool {
   521  		n++
   522  		return true
   523  	})
   524  	expect(t, n == 16)
   525  
   526  	n = 0
   527  	c.Within(rect, 3, nil, nil, func(o *object.Object) bool {
   528  		n++
   529  		return true
   530  	})
   531  	expect(t, n == 64)
   532  
   533  	n = 0
   534  	c.Within(rect, 3, nil, nil, func(o *object.Object) bool {
   535  		n++
   536  		return n <= 30
   537  	})
   538  	expect(t, n == 31)
   539  
   540  	n = 0
   541  	c.Intersects(rect, 3, nil, nil, func(o *object.Object) bool {
   542  		n++
   543  		return true
   544  	})
   545  	expect(t, n == 64)
   546  
   547  	n = 0
   548  	c.Intersects(rect, 3, nil, nil, func(o *object.Object) bool {
   549  		n++
   550  		return n <= 30
   551  	})
   552  	expect(t, n == 31)
   553  
   554  }
   555  
   556  func testCollectionVerifyContents(t *testing.T, c *Collection, objs map[string]geojson.Object) {
   557  	for id, o2 := range objs {
   558  		o := c.Get(id)
   559  		if o == nil {
   560  			t.Fatalf("ok[%s] = false, expect true", id)
   561  		}
   562  		j1 := string(o.Geo().AppendJSON(nil))
   563  		j2 := string(o2.AppendJSON(nil))
   564  		if j1 != j2 {
   565  			t.Fatalf("j1 == %s, expect %s", j1, j2)
   566  		}
   567  	}
   568  }
   569  
   570  func TestManyCollections(t *testing.T) {
   571  	colsM := make(map[string]*Collection)
   572  	cols := 100
   573  	objs := 1000
   574  	k := 0
   575  	for i := 0; i < cols; i++ {
   576  		key := strconv.FormatInt(int64(i), 10)
   577  		for j := 0; j < objs; j++ {
   578  			id := strconv.FormatInt(int64(j), 10)
   579  			p := geometry.Point{
   580  				X: rand.Float64()*360 - 180,
   581  				Y: rand.Float64()*180 - 90,
   582  			}
   583  			obj := geojson.Object(PO(p.X, p.Y))
   584  			col, ok := colsM[key]
   585  			if !ok {
   586  				col = New()
   587  				colsM[key] = col
   588  			}
   589  			col.Set(object.New(id, obj, 0, field.List{}))
   590  			k++
   591  		}
   592  	}
   593  
   594  	col := colsM["13"]
   595  	//println(col.Count())
   596  	bbox := geometry.Rect{
   597  		Min: geometry.Point{X: -180, Y: 30},
   598  		Max: geometry.Point{X: 34, Y: 100},
   599  	}
   600  	col.geoSearch(bbox, func(o *object.Object) bool {
   601  		//println(id)
   602  		return true
   603  	})
   604  }
   605  
   606  type testPointItem struct {
   607  	id     string
   608  	object geojson.Object
   609  	fields field.List
   610  }
   611  
   612  func makeBenchFields(nFields int) field.List {
   613  	var fields field.List
   614  	for i := 0; i < nFields; i++ {
   615  		key := fmt.Sprintf("%d", i)
   616  		val := key
   617  		fields = fields.Set(field.Make(key, val))
   618  	}
   619  	return fields
   620  }
   621  
   622  func BenchmarkInsert_Fields(t *testing.B) {
   623  	benchmarkInsert(t, 1)
   624  }
   625  
   626  func BenchmarkInsert_NoFields(t *testing.B) {
   627  	benchmarkInsert(t, 0)
   628  }
   629  
   630  func benchmarkInsert(t *testing.B, nFields int) {
   631  	rand.Seed(time.Now().UnixNano())
   632  	items := make([]testPointItem, t.N)
   633  	for i := 0; i < t.N; i++ {
   634  		items[i] = testPointItem{
   635  			fmt.Sprintf("%d", i),
   636  			PO(rand.Float64()*360-180, rand.Float64()*180-90),
   637  			makeBenchFields(nFields),
   638  		}
   639  	}
   640  	col := New()
   641  	t.ResetTimer()
   642  	for i := 0; i < t.N; i++ {
   643  		col.Set(object.New(items[i].id, items[i].object, 0, items[i].fields))
   644  	}
   645  }
   646  
   647  func BenchmarkReplace_Fields(t *testing.B) {
   648  	benchmarkReplace(t, 1)
   649  }
   650  
   651  func BenchmarkReplace_NoFields(t *testing.B) {
   652  	benchmarkReplace(t, 0)
   653  }
   654  
   655  func benchmarkReplace(t *testing.B, nFields int) {
   656  	rand.Seed(time.Now().UnixNano())
   657  	items := make([]testPointItem, t.N)
   658  	for i := 0; i < t.N; i++ {
   659  		items[i] = testPointItem{
   660  			fmt.Sprintf("%d", i),
   661  			PO(rand.Float64()*360-180, rand.Float64()*180-90),
   662  			makeBenchFields(nFields),
   663  		}
   664  	}
   665  	col := New()
   666  	for i := 0; i < t.N; i++ {
   667  		col.Set(object.New(items[i].id, items[i].object, 0, items[i].fields))
   668  	}
   669  	t.ResetTimer()
   670  	for _, i := range rand.Perm(t.N) {
   671  		o := col.Set(object.New(items[i].id, items[i].object, 0, field.List{}))
   672  		if o.Geo() != items[i].object {
   673  			t.Fatal("shoot!")
   674  		}
   675  	}
   676  }
   677  
   678  func BenchmarkGet_Fields(t *testing.B) {
   679  	benchmarkGet(t, 1)
   680  }
   681  
   682  func BenchmarkGet_NoFields(t *testing.B) {
   683  	benchmarkGet(t, 0)
   684  }
   685  
   686  func benchmarkGet(t *testing.B, nFields int) {
   687  	rand.Seed(time.Now().UnixNano())
   688  	items := make([]testPointItem, t.N)
   689  	for i := 0; i < t.N; i++ {
   690  		items[i] = testPointItem{
   691  			fmt.Sprintf("%d", i),
   692  			PO(rand.Float64()*360-180, rand.Float64()*180-90),
   693  			makeBenchFields(nFields),
   694  		}
   695  	}
   696  	col := New()
   697  	for i := 0; i < t.N; i++ {
   698  		col.Set(object.New(items[i].id, items[i].object, 0, items[i].fields))
   699  	}
   700  	t.ResetTimer()
   701  	for _, i := range rand.Perm(t.N) {
   702  		o := col.Get(items[i].id)
   703  		if o.Geo() != items[i].object {
   704  			t.Fatal("shoot!")
   705  		}
   706  	}
   707  }
   708  
   709  func BenchmarkRemove_Fields(t *testing.B) {
   710  	benchmarkRemove(t, 1)
   711  }
   712  
   713  func BenchmarkRemove_NoFields(t *testing.B) {
   714  	benchmarkRemove(t, 0)
   715  }
   716  
   717  func benchmarkRemove(t *testing.B, nFields int) {
   718  	rand.Seed(time.Now().UnixNano())
   719  	items := make([]testPointItem, t.N)
   720  	for i := 0; i < t.N; i++ {
   721  		items[i] = testPointItem{
   722  			fmt.Sprintf("%d", i),
   723  			PO(rand.Float64()*360-180, rand.Float64()*180-90),
   724  			makeBenchFields(nFields),
   725  		}
   726  	}
   727  	col := New()
   728  	for i := 0; i < t.N; i++ {
   729  		col.Set(object.New(items[i].id, items[i].object, 0, items[i].fields))
   730  	}
   731  	t.ResetTimer()
   732  	for _, i := range rand.Perm(t.N) {
   733  		prev := col.Delete(items[i].id)
   734  		if prev.Geo() != items[i].object {
   735  			t.Fatal("shoot!")
   736  		}
   737  	}
   738  }
   739  
   740  func BenchmarkScan_Fields(t *testing.B) {
   741  	benchmarkScan(t, 1)
   742  }
   743  
   744  func BenchmarkScan_NoFields(t *testing.B) {
   745  	benchmarkScan(t, 0)
   746  }
   747  
   748  func benchmarkScan(t *testing.B, nFields int) {
   749  	rand.Seed(time.Now().UnixNano())
   750  	items := make([]testPointItem, t.N)
   751  	for i := 0; i < t.N; i++ {
   752  		items[i] = testPointItem{
   753  			fmt.Sprintf("%d", i),
   754  			PO(rand.Float64()*360-180, rand.Float64()*180-90),
   755  			makeBenchFields(nFields),
   756  		}
   757  	}
   758  	col := New()
   759  	for i := 0; i < t.N; i++ {
   760  		col.Set(object.New(items[i].id, items[i].object, 0, items[i].fields))
   761  	}
   762  	t.ResetTimer()
   763  	for i := 0; i < t.N; i++ {
   764  		var scanIteration int
   765  		col.Scan(true, nil, nil, func(o *object.Object) bool {
   766  			scanIteration++
   767  			return scanIteration <= 500
   768  		})
   769  	}
   770  }