mirror of https://github.com/tidwall/tile38.git
Updated data structures to use Go generics.
Prior to this commit all objects in the Collection data structures were boxed in an Go interface{} which adds an extra 8 bytes per object and requires assertion to unbox. Go 1.18, released early 2022, introduced generics, which allows for storing the objects without boxing. This provides a extra boost in performance and lower in-memory footprint.
This commit is contained in:
parent
498bbe23ff
commit
cbfb271541
6
go.mod
6
go.mod
|
@ -18,7 +18,6 @@ require (
|
||||||
github.com/streadway/amqp v1.0.0
|
github.com/streadway/amqp v1.0.0
|
||||||
github.com/tidwall/btree v1.4.2
|
github.com/tidwall/btree v1.4.2
|
||||||
github.com/tidwall/buntdb v1.2.9
|
github.com/tidwall/buntdb v1.2.9
|
||||||
github.com/tidwall/geoindex v1.6.2
|
|
||||||
github.com/tidwall/geojson v1.3.4
|
github.com/tidwall/geojson v1.3.4
|
||||||
github.com/tidwall/gjson v1.12.1
|
github.com/tidwall/gjson v1.12.1
|
||||||
github.com/tidwall/match v1.1.1
|
github.com/tidwall/match v1.1.1
|
||||||
|
@ -26,7 +25,7 @@ require (
|
||||||
github.com/tidwall/redbench v0.1.0
|
github.com/tidwall/redbench v0.1.0
|
||||||
github.com/tidwall/redcon v1.4.4
|
github.com/tidwall/redcon v1.4.4
|
||||||
github.com/tidwall/resp v0.1.0
|
github.com/tidwall/resp v0.1.0
|
||||||
github.com/tidwall/rtree v1.7.1
|
github.com/tidwall/rtree v1.8.0
|
||||||
github.com/tidwall/sjson v1.2.4
|
github.com/tidwall/sjson v1.2.4
|
||||||
github.com/xdg/scram v1.0.5
|
github.com/xdg/scram v1.0.5
|
||||||
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da
|
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da
|
||||||
|
@ -88,9 +87,8 @@ require (
|
||||||
github.com/prometheus/common v0.32.1 // indirect
|
github.com/prometheus/common v0.32.1 // indirect
|
||||||
github.com/prometheus/procfs v0.7.3 // indirect
|
github.com/prometheus/procfs v0.7.3 // indirect
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||||
github.com/tidwall/cities v0.1.0 // indirect
|
github.com/tidwall/geoindex v1.7.0 // indirect
|
||||||
github.com/tidwall/grect v0.1.4 // indirect
|
github.com/tidwall/grect v0.1.4 // indirect
|
||||||
github.com/tidwall/lotsa v1.0.2 // indirect
|
|
||||||
github.com/tidwall/rtred v0.1.2 // indirect
|
github.com/tidwall/rtred v0.1.2 // indirect
|
||||||
github.com/tidwall/tinyqueue v0.1.1 // indirect
|
github.com/tidwall/tinyqueue v0.1.1 // indirect
|
||||||
github.com/xdg/stringprep v1.0.3 // indirect
|
github.com/xdg/stringprep v1.0.3 // indirect
|
||||||
|
|
8
go.sum
8
go.sum
|
@ -357,8 +357,8 @@ github.com/tidwall/buntdb v1.2.9/go.mod h1:IwyGSvvDg6hnKSIhtdZ0AqhCZGH8ukdtCAzaP
|
||||||
github.com/tidwall/cities v0.1.0 h1:CVNkmMf7NEC9Bvokf5GoSsArHCKRMTgLuubRTHnH0mE=
|
github.com/tidwall/cities v0.1.0 h1:CVNkmMf7NEC9Bvokf5GoSsArHCKRMTgLuubRTHnH0mE=
|
||||||
github.com/tidwall/cities v0.1.0/go.mod h1:lV/HDp2gCcRcHJWqgt6Di54GiDrTZwh1aG2ZUPNbqa4=
|
github.com/tidwall/cities v0.1.0/go.mod h1:lV/HDp2gCcRcHJWqgt6Di54GiDrTZwh1aG2ZUPNbqa4=
|
||||||
github.com/tidwall/geoindex v1.4.4/go.mod h1:rvVVNEFfkJVWGUdEfU8QaoOg/9zFX0h9ofWzA60mz1I=
|
github.com/tidwall/geoindex v1.4.4/go.mod h1:rvVVNEFfkJVWGUdEfU8QaoOg/9zFX0h9ofWzA60mz1I=
|
||||||
github.com/tidwall/geoindex v1.6.2 h1:cWbqC9HFXMxc2p6KMWbs9VG6/gnrfC53EIPQEMcXO1g=
|
github.com/tidwall/geoindex v1.7.0 h1:jtk41sfgwIt8MEDyC3xyKSj75iXXf6rjReJGDNPtR5o=
|
||||||
github.com/tidwall/geoindex v1.6.2/go.mod h1:rvVVNEFfkJVWGUdEfU8QaoOg/9zFX0h9ofWzA60mz1I=
|
github.com/tidwall/geoindex v1.7.0/go.mod h1:rvVVNEFfkJVWGUdEfU8QaoOg/9zFX0h9ofWzA60mz1I=
|
||||||
github.com/tidwall/geojson v1.3.4 h1:mHB2yGK7HPgf4vFkLdPeIzguFpqkmCT2yTgGhXbrqBo=
|
github.com/tidwall/geojson v1.3.4 h1:mHB2yGK7HPgf4vFkLdPeIzguFpqkmCT2yTgGhXbrqBo=
|
||||||
github.com/tidwall/geojson v1.3.4/go.mod h1:1cn3UWfSYCJOq53NZoQ9rirdw89+DM0vw+ZOAVvuReg=
|
github.com/tidwall/geojson v1.3.4/go.mod h1:1cn3UWfSYCJOq53NZoQ9rirdw89+DM0vw+ZOAVvuReg=
|
||||||
github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo=
|
github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo=
|
||||||
|
@ -380,8 +380,8 @@ github.com/tidwall/resp v0.1.0/go.mod h1:18xEj855iMY2bK6tNF2A4x+nZy5gWO1iO7OOl3j
|
||||||
github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8=
|
github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8=
|
||||||
github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ=
|
github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ=
|
||||||
github.com/tidwall/rtree v1.3.1/go.mod h1:S+JSsqPTI8LfWA4xHBo5eXzie8WJLVFeppAutSegl6M=
|
github.com/tidwall/rtree v1.3.1/go.mod h1:S+JSsqPTI8LfWA4xHBo5eXzie8WJLVFeppAutSegl6M=
|
||||||
github.com/tidwall/rtree v1.7.1 h1:rv3Q8RBKH2HbJ6DsqpfrXfV9l+dCT1jupTpDiceHN3I=
|
github.com/tidwall/rtree v1.8.0 h1:nYVLh9UKJrd4CZCNawD3WbHNxmI9LYR4j3E2hqO3tjQ=
|
||||||
github.com/tidwall/rtree v1.7.1/go.mod h1:39+jGCj9hYqhflezmsTBOlysIk09ytm+8EQsC/E/2X0=
|
github.com/tidwall/rtree v1.8.0/go.mod h1:iDJQ9NBRtbfKkzZu02za+mIlaP+bjYPnunbSNidpbCQ=
|
||||||
github.com/tidwall/sjson v1.2.4 h1:cuiLzLnaMeBhRmEv00Lpk3tkYrcxpmbU81tAY4Dw0tc=
|
github.com/tidwall/sjson v1.2.4 h1:cuiLzLnaMeBhRmEv00Lpk3tkYrcxpmbU81tAY4Dw0tc=
|
||||||
github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM=
|
github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM=
|
||||||
github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE=
|
github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE=
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/tidwall/btree"
|
"github.com/tidwall/btree"
|
||||||
"github.com/tidwall/geoindex"
|
|
||||||
"github.com/tidwall/geojson"
|
"github.com/tidwall/geojson"
|
||||||
"github.com/tidwall/geojson/geo"
|
"github.com/tidwall/geojson/geo"
|
||||||
"github.com/tidwall/geojson/geometry"
|
"github.com/tidwall/geojson/geometry"
|
||||||
|
@ -28,13 +27,13 @@ type itemT struct {
|
||||||
fieldValuesSlot fieldValuesSlot
|
fieldValuesSlot fieldValuesSlot
|
||||||
}
|
}
|
||||||
|
|
||||||
func byID(a, b interface{}) bool {
|
func byID(a, b *itemT) bool {
|
||||||
return a.(*itemT).id < b.(*itemT).id
|
return a.id < b.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func byValue(a, b interface{}) bool {
|
func byValue(a, b *itemT) bool {
|
||||||
value1 := a.(*itemT).obj.String()
|
value1 := a.obj.String()
|
||||||
value2 := b.(*itemT).obj.String()
|
value2 := b.obj.String()
|
||||||
if value1 < value2 {
|
if value1 < value2 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -45,13 +44,11 @@ func byValue(a, b interface{}) bool {
|
||||||
return byID(a, b)
|
return byID(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func byExpires(a, b interface{}) bool {
|
func byExpires(a, b *itemT) bool {
|
||||||
item1 := a.(*itemT)
|
if a.expires < b.expires {
|
||||||
item2 := b.(*itemT)
|
|
||||||
if item1.expires < item2.expires {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if item1.expires > item2.expires {
|
if a.expires > b.expires {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// the values match so we'll compare IDs, which are always unique.
|
// the values match so we'll compare IDs, which are always unique.
|
||||||
|
@ -60,10 +57,10 @@ func byExpires(a, b interface{}) bool {
|
||||||
|
|
||||||
// Collection represents a collection of geojson objects.
|
// Collection represents a collection of geojson objects.
|
||||||
type Collection struct {
|
type Collection struct {
|
||||||
items *btree.BTree // items sorted by id
|
items *btree.BTreeG[*itemT] // items sorted by id
|
||||||
index *geoindex.Index // items geospatially indexed
|
spatial *rtree.RTreeG[*itemT] // items geospatially indexed
|
||||||
values *btree.BTree // items sorted by value+id
|
values *btree.BTreeG[*itemT] // items sorted by value+id
|
||||||
expires *btree.BTree // items sorted by ex+id
|
expires *btree.BTreeG[*itemT] // items sorted by ex+id
|
||||||
fieldMap map[string]int
|
fieldMap map[string]int
|
||||||
fieldArr []string
|
fieldArr []string
|
||||||
fieldValues *fieldValues
|
fieldValues *fieldValues
|
||||||
|
@ -73,13 +70,15 @@ type Collection struct {
|
||||||
nobjects int // non-geometry count
|
nobjects int // non-geometry count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var optsNoLock = btree.Options{NoLocks: true}
|
||||||
|
|
||||||
// New creates an empty collection
|
// New creates an empty collection
|
||||||
func New() *Collection {
|
func New() *Collection {
|
||||||
col := &Collection{
|
col := &Collection{
|
||||||
items: btree.NewNonConcurrent(byID),
|
items: btree.NewBTreeGOptions(byID, optsNoLock),
|
||||||
index: geoindex.Wrap(&rtree.RTree{}),
|
values: btree.NewBTreeGOptions(byValue, optsNoLock),
|
||||||
values: btree.NewNonConcurrent(byValue),
|
expires: btree.NewBTreeGOptions(byExpires, optsNoLock),
|
||||||
expires: btree.NewNonConcurrent(byExpires),
|
spatial: &rtree.RTreeG[*itemT]{},
|
||||||
fieldMap: make(map[string]int),
|
fieldMap: make(map[string]int),
|
||||||
fieldArr: make([]string, 0),
|
fieldArr: make([]string, 0),
|
||||||
fieldValues: &fieldValues{},
|
fieldValues: &fieldValues{},
|
||||||
|
@ -109,7 +108,7 @@ func (c *Collection) TotalWeight() int {
|
||||||
|
|
||||||
// Bounds returns the bounds of all the items in the collection.
|
// Bounds returns the bounds of all the items in the collection.
|
||||||
func (c *Collection) Bounds() (minX, minY, maxX, maxY float64) {
|
func (c *Collection) Bounds() (minX, minY, maxX, maxY float64) {
|
||||||
min, max := c.index.Bounds()
|
min, max := c.spatial.Bounds()
|
||||||
if len(min) >= 2 && len(max) >= 2 {
|
if len(min) >= 2 && len(max) >= 2 {
|
||||||
return min[0], min[1], max[0], max[1]
|
return min[0], min[1], max[0], max[1]
|
||||||
}
|
}
|
||||||
|
@ -134,7 +133,7 @@ func (c *Collection) objWeight(item *itemT) int {
|
||||||
func (c *Collection) indexDelete(item *itemT) {
|
func (c *Collection) indexDelete(item *itemT) {
|
||||||
if !item.obj.Empty() {
|
if !item.obj.Empty() {
|
||||||
rect := item.obj.Rect()
|
rect := item.obj.Rect()
|
||||||
c.index.Delete(
|
c.spatial.Delete(
|
||||||
[2]float64{rect.Min.X, rect.Min.Y},
|
[2]float64{rect.Min.X, rect.Min.Y},
|
||||||
[2]float64{rect.Max.X, rect.Max.Y},
|
[2]float64{rect.Max.X, rect.Max.Y},
|
||||||
item)
|
item)
|
||||||
|
@ -144,7 +143,7 @@ func (c *Collection) indexDelete(item *itemT) {
|
||||||
func (c *Collection) indexInsert(item *itemT) {
|
func (c *Collection) indexInsert(item *itemT) {
|
||||||
if !item.obj.Empty() {
|
if !item.obj.Empty() {
|
||||||
rect := item.obj.Rect()
|
rect := item.obj.Rect()
|
||||||
c.index.Insert(
|
c.spatial.Insert(
|
||||||
[2]float64{rect.Min.X, rect.Min.Y},
|
[2]float64{rect.Min.X, rect.Min.Y},
|
||||||
[2]float64{rect.Max.X, rect.Max.Y},
|
[2]float64{rect.Max.X, rect.Max.Y},
|
||||||
item)
|
item)
|
||||||
|
@ -164,9 +163,8 @@ func (c *Collection) Set(
|
||||||
newItem := &itemT{id: id, obj: obj, fieldValuesSlot: nilValuesSlot, expires: ex}
|
newItem := &itemT{id: id, obj: obj, fieldValuesSlot: nilValuesSlot, expires: ex}
|
||||||
|
|
||||||
// add the new item to main btree and remove the old one if needed
|
// add the new item to main btree and remove the old one if needed
|
||||||
oldItem := c.items.Set(newItem)
|
oldItem, ok := c.items.Set(newItem)
|
||||||
if oldItem != nil {
|
if ok {
|
||||||
oldItem := oldItem.(*itemT)
|
|
||||||
// the old item was removed, now let's remove it from the rtree/btree.
|
// the old item was removed, now let's remove it from the rtree/btree.
|
||||||
if objIsSpatial(oldItem.obj) {
|
if objIsSpatial(oldItem.obj) {
|
||||||
c.indexDelete(oldItem)
|
c.indexDelete(oldItem)
|
||||||
|
@ -232,11 +230,10 @@ func (c *Collection) Set(
|
||||||
func (c *Collection) Delete(id string) (
|
func (c *Collection) Delete(id string) (
|
||||||
obj geojson.Object, fields []float64, ok bool,
|
obj geojson.Object, fields []float64, ok bool,
|
||||||
) {
|
) {
|
||||||
v := c.items.Delete(&itemT{id: id})
|
oldItem, ok := c.items.Delete(&itemT{id: id})
|
||||||
if v == nil {
|
if !ok {
|
||||||
return nil, nil, false
|
return nil, nil, false
|
||||||
}
|
}
|
||||||
oldItem := v.(*itemT)
|
|
||||||
if objIsSpatial(oldItem.obj) {
|
if objIsSpatial(oldItem.obj) {
|
||||||
if !oldItem.obj.Empty() {
|
if !oldItem.obj.Empty() {
|
||||||
c.indexDelete(oldItem)
|
c.indexDelete(oldItem)
|
||||||
|
@ -263,20 +260,18 @@ func (c *Collection) Delete(id string) (
|
||||||
func (c *Collection) Get(id string) (
|
func (c *Collection) Get(id string) (
|
||||||
obj geojson.Object, fields []float64, ex int64, ok bool,
|
obj geojson.Object, fields []float64, ex int64, ok bool,
|
||||||
) {
|
) {
|
||||||
itemV := c.items.Get(&itemT{id: id})
|
item, ok := c.items.Get(&itemT{id: id})
|
||||||
if itemV == nil {
|
if !ok {
|
||||||
return nil, nil, 0, false
|
return nil, nil, 0, false
|
||||||
}
|
}
|
||||||
item := itemV.(*itemT)
|
|
||||||
return item.obj, c.fieldValues.get(item.fieldValuesSlot), item.expires, true
|
return item.obj, c.fieldValues.get(item.fieldValuesSlot), item.expires, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Collection) SetExpires(id string, ex int64) bool {
|
func (c *Collection) SetExpires(id string, ex int64) bool {
|
||||||
v := c.items.Get(&itemT{id: id})
|
item, ok := c.items.Get(&itemT{id: id})
|
||||||
if v == nil {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
item := v.(*itemT)
|
|
||||||
if item.expires != 0 {
|
if item.expires != 0 {
|
||||||
c.expires.Delete(item)
|
c.expires.Delete(item)
|
||||||
}
|
}
|
||||||
|
@ -292,11 +287,10 @@ func (c *Collection) SetExpires(id string, ex int64) bool {
|
||||||
func (c *Collection) SetField(id, field string, value float64) (
|
func (c *Collection) SetField(id, field string, value float64) (
|
||||||
obj geojson.Object, fields []float64, updated bool, ok bool,
|
obj geojson.Object, fields []float64, updated bool, ok bool,
|
||||||
) {
|
) {
|
||||||
itemV := c.items.Get(&itemT{id: id})
|
item, ok := c.items.Get(&itemT{id: id})
|
||||||
if itemV == nil {
|
if !ok {
|
||||||
return nil, nil, false, false
|
return nil, nil, false, false
|
||||||
}
|
}
|
||||||
item := itemV.(*itemT)
|
|
||||||
_, updateCount, weightDelta := c.setFieldValues(item, []string{field}, []float64{value})
|
_, updateCount, weightDelta := c.setFieldValues(item, []string{field}, []float64{value})
|
||||||
c.weight += weightDelta
|
c.weight += weightDelta
|
||||||
return item.obj, c.fieldValues.get(item.fieldValuesSlot), updateCount > 0, true
|
return item.obj, c.fieldValues.get(item.fieldValuesSlot), updateCount > 0, true
|
||||||
|
@ -306,11 +300,10 @@ func (c *Collection) SetField(id, field string, value float64) (
|
||||||
func (c *Collection) SetFields(
|
func (c *Collection) SetFields(
|
||||||
id string, inFields []string, inValues []float64,
|
id string, inFields []string, inValues []float64,
|
||||||
) (obj geojson.Object, fields []float64, updatedCount int, ok bool) {
|
) (obj geojson.Object, fields []float64, updatedCount int, ok bool) {
|
||||||
itemV := c.items.Get(&itemT{id: id})
|
item, ok := c.items.Get(&itemT{id: id})
|
||||||
if itemV == nil {
|
if !ok {
|
||||||
return nil, nil, 0, false
|
return nil, nil, 0, false
|
||||||
}
|
}
|
||||||
item := itemV.(*itemT)
|
|
||||||
newFieldValues, updateCount, weightDelta := c.setFieldValues(item, inFields, inValues)
|
newFieldValues, updateCount, weightDelta := c.setFieldValues(item, inFields, inValues)
|
||||||
c.weight += weightDelta
|
c.weight += weightDelta
|
||||||
return item.obj, newFieldValues, updateCount, true
|
return item.obj, newFieldValues, updateCount, true
|
||||||
|
@ -394,20 +387,19 @@ func (c *Collection) Scan(
|
||||||
offset = cursor.Offset()
|
offset = cursor.Offset()
|
||||||
cursor.Step(offset)
|
cursor.Step(offset)
|
||||||
}
|
}
|
||||||
iter := func(item interface{}) bool {
|
iter := func(item *itemT) bool {
|
||||||
count++
|
count++
|
||||||
if count <= offset {
|
if count <= offset {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
nextStep(count, cursor, deadline)
|
nextStep(count, cursor, deadline)
|
||||||
iitm := item.(*itemT)
|
keepon = iterator(item.id, item.obj, c.fieldValues.get(item.fieldValuesSlot))
|
||||||
keepon = iterator(iitm.id, iitm.obj, c.fieldValues.get(iitm.fieldValuesSlot))
|
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
if desc {
|
if desc {
|
||||||
c.items.Descend(nil, iter)
|
c.items.Reverse(iter)
|
||||||
} else {
|
} else {
|
||||||
c.items.Ascend(nil, iter)
|
c.items.Scan(iter)
|
||||||
}
|
}
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
|
@ -427,8 +419,7 @@ func (c *Collection) ScanRange(
|
||||||
offset = cursor.Offset()
|
offset = cursor.Offset()
|
||||||
cursor.Step(offset)
|
cursor.Step(offset)
|
||||||
}
|
}
|
||||||
iter := func(value interface{}) bool {
|
iter := func(item *itemT) bool {
|
||||||
item := value.(*itemT)
|
|
||||||
count++
|
count++
|
||||||
if count <= offset {
|
if count <= offset {
|
||||||
return true
|
return true
|
||||||
|
@ -443,8 +434,7 @@ func (c *Collection) ScanRange(
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
iitm := value.(*itemT)
|
keepon = iterator(item.id, item.obj, c.fieldValues.get(item.fieldValuesSlot))
|
||||||
keepon = iterator(iitm.id, iitm.obj, c.fieldValues.get(iitm.fieldValuesSlot))
|
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,20 +460,19 @@ func (c *Collection) SearchValues(
|
||||||
offset = cursor.Offset()
|
offset = cursor.Offset()
|
||||||
cursor.Step(offset)
|
cursor.Step(offset)
|
||||||
}
|
}
|
||||||
iter := func(item interface{}) bool {
|
iter := func(item *itemT) bool {
|
||||||
count++
|
count++
|
||||||
if count <= offset {
|
if count <= offset {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
nextStep(count, cursor, deadline)
|
nextStep(count, cursor, deadline)
|
||||||
iitm := item.(*itemT)
|
keepon = iterator(item.id, item.obj, c.fieldValues.get(item.fieldValuesSlot))
|
||||||
keepon = iterator(iitm.id, iitm.obj, c.fieldValues.get(iitm.fieldValuesSlot))
|
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
if desc {
|
if desc {
|
||||||
c.values.Descend(nil, iter)
|
c.values.Reverse(iter)
|
||||||
} else {
|
} else {
|
||||||
c.values.Ascend(nil, iter)
|
c.values.Scan(iter)
|
||||||
}
|
}
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
|
@ -501,33 +490,32 @@ func (c *Collection) SearchValuesRange(start, end string, desc bool,
|
||||||
offset = cursor.Offset()
|
offset = cursor.Offset()
|
||||||
cursor.Step(offset)
|
cursor.Step(offset)
|
||||||
}
|
}
|
||||||
iter := func(item interface{}) bool {
|
iter := func(item *itemT) bool {
|
||||||
count++
|
count++
|
||||||
if count <= offset {
|
if count <= offset {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
nextStep(count, cursor, deadline)
|
nextStep(count, cursor, deadline)
|
||||||
iitm := item.(*itemT)
|
keepon = iterator(item.id, item.obj, c.fieldValues.get(item.fieldValuesSlot))
|
||||||
keepon = iterator(iitm.id, iitm.obj, c.fieldValues.get(iitm.fieldValuesSlot))
|
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
pstart := &itemT{obj: String(start)}
|
pstart := &itemT{obj: String(start)}
|
||||||
pend := &itemT{obj: String(end)}
|
pend := &itemT{obj: String(end)}
|
||||||
if desc {
|
if desc {
|
||||||
// descend range
|
// descend range
|
||||||
c.values.Descend(pstart, func(item interface{}) bool {
|
c.values.Descend(pstart, func(item *itemT) bool {
|
||||||
return bGT(c.values, item, pend) && iter(item)
|
return bGT(c.values, item, pend) && iter(item)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
c.values.Ascend(pstart, func(item interface{}) bool {
|
c.values.Ascend(pstart, func(item *itemT) bool {
|
||||||
return bLT(c.values, item, pend) && iter(item)
|
return bLT(c.values, item, pend) && iter(item)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
|
|
||||||
func bLT(tr *btree.BTree, a, b interface{}) bool { return tr.Less(a, b) }
|
func bLT(tr *btree.BTreeG[*itemT], a, b *itemT) bool { return tr.Less(a, b) }
|
||||||
func bGT(tr *btree.BTree, a, b interface{}) bool { return tr.Less(b, a) }
|
func bGT(tr *btree.BTreeG[*itemT], a, b *itemT) bool { return tr.Less(b, a) }
|
||||||
|
|
||||||
// ScanGreaterOrEqual iterates though the collection starting with specified id.
|
// ScanGreaterOrEqual iterates though the collection starting with specified id.
|
||||||
func (c *Collection) ScanGreaterOrEqual(id string, desc bool,
|
func (c *Collection) ScanGreaterOrEqual(id string, desc bool,
|
||||||
|
@ -542,13 +530,12 @@ func (c *Collection) ScanGreaterOrEqual(id string, desc bool,
|
||||||
offset = cursor.Offset()
|
offset = cursor.Offset()
|
||||||
cursor.Step(offset)
|
cursor.Step(offset)
|
||||||
}
|
}
|
||||||
iter := func(v interface{}) bool {
|
iter := func(item *itemT) bool {
|
||||||
count++
|
count++
|
||||||
if count <= offset {
|
if count <= offset {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
nextStep(count, cursor, deadline)
|
nextStep(count, cursor, deadline)
|
||||||
item := v.(*itemT)
|
|
||||||
keepon = iterator(item.id, item.obj, c.fieldValues.get(item.fieldValuesSlot), item.expires)
|
keepon = iterator(item.id, item.obj, c.fieldValues.get(item.fieldValuesSlot), item.expires)
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
|
@ -565,11 +552,10 @@ func (c *Collection) geoSearch(
|
||||||
iter func(id string, obj geojson.Object, fields []float64) bool,
|
iter func(id string, obj geojson.Object, fields []float64) bool,
|
||||||
) bool {
|
) bool {
|
||||||
alive := true
|
alive := true
|
||||||
c.index.Search(
|
c.spatial.Search(
|
||||||
[2]float64{rect.Min.X, rect.Min.Y},
|
[2]float64{rect.Min.X, rect.Min.Y},
|
||||||
[2]float64{rect.Max.X, rect.Max.Y},
|
[2]float64{rect.Max.X, rect.Max.Y},
|
||||||
func(_, _ [2]float64, itemv interface{}) bool {
|
func(_, _ [2]float64, item *itemT) bool {
|
||||||
item := itemv.(*itemT)
|
|
||||||
alive = iter(item.id, item.obj, c.fieldValues.get(item.fieldValuesSlot))
|
alive = iter(item.id, item.obj, c.fieldValues.get(item.fieldValuesSlot))
|
||||||
return alive
|
return alive
|
||||||
},
|
},
|
||||||
|
@ -755,10 +741,10 @@ func (c *Collection) Nearby(
|
||||||
minLat, minLon, maxLat, maxLon :=
|
minLat, minLon, maxLat, maxLon :=
|
||||||
geo.RectFromCenter(center.Y, center.X, meters)
|
geo.RectFromCenter(center.Y, center.X, meters)
|
||||||
var exists bool
|
var exists bool
|
||||||
c.index.Search(
|
c.spatial.Search(
|
||||||
[2]float64{minLon, minLat},
|
[2]float64{minLon, minLat},
|
||||||
[2]float64{maxLon, maxLat},
|
[2]float64{maxLon, maxLat},
|
||||||
func(_, _ [2]float64, itemv interface{}) bool {
|
func(_, _ [2]float64, item *itemT) bool {
|
||||||
exists = true
|
exists = true
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
|
@ -778,15 +764,14 @@ func (c *Collection) Nearby(
|
||||||
offset = cursor.Offset()
|
offset = cursor.Offset()
|
||||||
cursor.Step(offset)
|
cursor.Step(offset)
|
||||||
}
|
}
|
||||||
c.index.Nearby(
|
c.spatial.Nearby(
|
||||||
geodeticDistAlgo([2]float64{center.X, center.Y}),
|
geodeticDistAlgo[*itemT]([2]float64{center.X, center.Y}),
|
||||||
func(_, _ [2]float64, itemv interface{}, dist float64) bool {
|
func(_, _ [2]float64, item *itemT, dist float64) bool {
|
||||||
count++
|
count++
|
||||||
if count <= offset {
|
if count <= offset {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
nextStep(count, cursor, deadline)
|
nextStep(count, cursor, deadline)
|
||||||
item := itemv.(*itemT)
|
|
||||||
alive = iter(item.id, item.obj, c.fieldValues.get(item.fieldValuesSlot), dist)
|
alive = iter(item.id, item.obj, c.fieldValues.get(item.fieldValuesSlot), dist)
|
||||||
return alive
|
return alive
|
||||||
},
|
},
|
||||||
|
@ -804,17 +789,10 @@ func nextStep(step uint64, cursor Cursor, deadline *deadline.Deadline) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Expired struct {
|
|
||||||
ID string
|
|
||||||
Obj geojson.Object
|
|
||||||
Fields []float64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expired returns a list of all objects that have expired.
|
// Expired returns a list of all objects that have expired.
|
||||||
func (c *Collection) Expired(now int64, buffer []string) (ids []string) {
|
func (c *Collection) Expired(now int64, buffer []string) (ids []string) {
|
||||||
ids = buffer[:0]
|
ids = buffer[:0]
|
||||||
c.expires.Ascend(nil, func(v interface{}) bool {
|
c.expires.Scan(func(item *itemT) bool {
|
||||||
item := v.(*itemT)
|
|
||||||
if now < item.expires {
|
if now < item.expires {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,11 @@ package collection
|
||||||
|
|
||||||
import "math"
|
import "math"
|
||||||
|
|
||||||
func geodeticDistAlgo(center [2]float64) (
|
func geodeticDistAlgo[T any](center [2]float64) (
|
||||||
algo func(min, max [2]float64, data interface{}, item bool) (dist float64),
|
algo func(min, max [2]float64, data T, item bool) (dist float64),
|
||||||
) {
|
) {
|
||||||
const earthRadius = 6371e3
|
const earthRadius = 6371e3
|
||||||
return func(min, max [2]float64, data interface{}, item bool) (dist float64) {
|
return func(min, max [2]float64, data T, item bool) (dist float64) {
|
||||||
return earthRadius * pointRectDistGeodeticDeg(
|
return earthRadius * pointRectDistGeodeticDeg(
|
||||||
center[1], center[0],
|
center[1], center[0],
|
||||||
min[1], min[0],
|
min[1], min[0],
|
||||||
|
|
|
@ -7,83 +7,67 @@ import (
|
||||||
"github.com/tidwall/geojson/geometry"
|
"github.com/tidwall/geojson/geometry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// String ...
|
|
||||||
type String string
|
type String string
|
||||||
|
|
||||||
var _ geojson.Object = String("")
|
var _ geojson.Object = String("")
|
||||||
|
|
||||||
// Spatial ...
|
|
||||||
func (s String) Spatial() geojson.Spatial {
|
func (s String) Spatial() geojson.Spatial {
|
||||||
return geojson.EmptySpatial{}
|
return geojson.EmptySpatial{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForEach ...
|
|
||||||
func (s String) ForEach(iter func(geom geojson.Object) bool) bool {
|
func (s String) ForEach(iter func(geom geojson.Object) bool) bool {
|
||||||
return iter(s)
|
return iter(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty ...
|
|
||||||
func (s String) Empty() bool {
|
func (s String) Empty() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid ...
|
|
||||||
func (s String) Valid() bool {
|
func (s String) Valid() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rect ...
|
|
||||||
func (s String) Rect() geometry.Rect {
|
func (s String) Rect() geometry.Rect {
|
||||||
return geometry.Rect{}
|
return geometry.Rect{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Center ...
|
|
||||||
func (s String) Center() geometry.Point {
|
func (s String) Center() geometry.Point {
|
||||||
return geometry.Point{}
|
return geometry.Point{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendJSON ...
|
|
||||||
func (s String) AppendJSON(dst []byte) []byte {
|
func (s String) AppendJSON(dst []byte) []byte {
|
||||||
data, _ := json.Marshal(string(s))
|
data, _ := json.Marshal(string(s))
|
||||||
return append(dst, data...)
|
return append(dst, data...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// String ...
|
|
||||||
func (s String) String() string {
|
func (s String) String() string {
|
||||||
return string(s)
|
return string(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON ...
|
|
||||||
func (s String) JSON() string {
|
func (s String) JSON() string {
|
||||||
return string(s.AppendJSON(nil))
|
return string(s.AppendJSON(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON ...
|
|
||||||
func (s String) MarshalJSON() ([]byte, error) {
|
func (s String) MarshalJSON() ([]byte, error) {
|
||||||
return s.AppendJSON(nil), nil
|
return s.AppendJSON(nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Within ...
|
|
||||||
func (s String) Within(obj geojson.Object) bool {
|
func (s String) Within(obj geojson.Object) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains ...
|
|
||||||
func (s String) Contains(obj geojson.Object) bool {
|
func (s String) Contains(obj geojson.Object) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intersects ...
|
|
||||||
func (s String) Intersects(obj geojson.Object) bool {
|
func (s String) Intersects(obj geojson.Object) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// NumPoints ...
|
|
||||||
func (s String) NumPoints() int {
|
func (s String) NumPoints() int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Distance ...
|
|
||||||
func (s String) Distance(obj geojson.Object) float64 {
|
func (s String) Distance(obj geojson.Object) float64 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue