mirror of https://github.com/tidwall/tile38.git
Isolated fields type
This commit is contained in:
parent
3ac8dc2ffb
commit
a60dc57598
|
@ -119,39 +119,39 @@ func (c *Collection) delItem(item *item.Item) {
|
||||||
func (c *Collection) Set(
|
func (c *Collection) Set(
|
||||||
id string, obj geojson.Object, fields []string, values []float64,
|
id string, obj geojson.Object, fields []string, values []float64,
|
||||||
) (
|
) (
|
||||||
oldObj geojson.Object, oldFields []float64, newFields []float64,
|
oldObj geojson.Object, oldFields *Fields, newFields *Fields,
|
||||||
) {
|
) {
|
||||||
// create the new item
|
// create the new item
|
||||||
item := item.New(id, obj)
|
newItem := item.New(id, obj)
|
||||||
|
|
||||||
// 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
|
||||||
oldItemV, ok := c.items.Set(item)
|
var oldItem *item.Item
|
||||||
|
oldItemV, ok := c.items.Set(newItem)
|
||||||
if ok {
|
if ok {
|
||||||
oldItem := oldItemV
|
oldItem = oldItemV
|
||||||
oldObj = oldItem.Obj()
|
oldObj = oldItem.Obj()
|
||||||
|
|
||||||
// remove old item from indexes
|
// remove old item from indexes
|
||||||
c.delItem(oldItem)
|
c.delItem(oldItem)
|
||||||
if len(oldItem.Fields()) > 0 {
|
if oldItem.HasFields() {
|
||||||
// merge old and new fields
|
// merge old and new fields
|
||||||
oldFields = oldItem.Fields()
|
newItem.CopyOverFields(oldItem)
|
||||||
item.CopyOverFields(oldFields)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if fields == nil && len(values) > 0 {
|
if fields == nil && len(values) > 0 {
|
||||||
// directly set the field values, from copy
|
// directly set the field values, from copy
|
||||||
item.CopyOverFields(values)
|
newItem.CopyOverFields(values)
|
||||||
} else if len(fields) > 0 {
|
} else if len(fields) > 0 {
|
||||||
// add new field to new item
|
// add new field to new item
|
||||||
c.setFields(item, fields, values, false)
|
c.setFields(newItem, fields, values, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// add new item to indexes
|
// add new item to indexes
|
||||||
c.addItem(item)
|
c.addItem(newItem)
|
||||||
// fmt.Printf("!!! %#v\n", oldObj)
|
// fmt.Printf("!!! %#v\n", oldObj)
|
||||||
|
|
||||||
return oldObj, oldFields, item.Fields()
|
return oldObj, itemFields(oldItem), itemFields(newItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Collection) setFields(
|
func (c *Collection) setFields(
|
||||||
|
@ -192,7 +192,7 @@ func (c *Collection) setField(
|
||||||
// Delete removes an object and returns it.
|
// Delete removes an object and returns it.
|
||||||
// If the object does not exist then the 'ok' return value will be false.
|
// If the object does not exist then the 'ok' return value will be false.
|
||||||
func (c *Collection) Delete(id string) (
|
func (c *Collection) Delete(id string) (
|
||||||
obj geojson.Object, fields []float64, ok bool,
|
obj geojson.Object, fields *Fields, ok bool,
|
||||||
) {
|
) {
|
||||||
oldItemV, ok := c.items.Delete(id)
|
oldItemV, ok := c.items.Delete(id)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -202,13 +202,13 @@ func (c *Collection) Delete(id string) (
|
||||||
|
|
||||||
c.delItem(oldItem)
|
c.delItem(oldItem)
|
||||||
|
|
||||||
return oldItem.Obj(), oldItem.Fields(), true
|
return oldItem.Obj(), itemFields(oldItem), true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns an object.
|
// Get returns an object.
|
||||||
// If the object does not exist then the 'ok' return value will be false.
|
// If the object does not exist then the 'ok' return value will be false.
|
||||||
func (c *Collection) Get(id string) (
|
func (c *Collection) Get(id string) (
|
||||||
obj geojson.Object, fields []float64, ok bool,
|
obj geojson.Object, fields *Fields, ok bool,
|
||||||
) {
|
) {
|
||||||
itemV, ok := c.items.Get(id)
|
itemV, ok := c.items.Get(id)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -216,13 +216,13 @@ func (c *Collection) Get(id string) (
|
||||||
}
|
}
|
||||||
item := itemV
|
item := itemV
|
||||||
|
|
||||||
return item.Obj(), item.Fields(), true
|
return item.Obj(), itemFields(item), true
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetField set a field value for an object and returns that object.
|
// SetField set a field value for an object and returns that object.
|
||||||
// If the object does not exist then the 'ok' return value will be false.
|
// If the object does not exist then the 'ok' return value will be false.
|
||||||
func (c *Collection) SetField(id, fieldName string, fieldValue float64) (
|
func (c *Collection) SetField(id, fieldName string, fieldValue float64) (
|
||||||
obj geojson.Object, fields []float64, updated bool, ok bool,
|
obj geojson.Object, fields *Fields, updated bool, ok bool,
|
||||||
) {
|
) {
|
||||||
itemV, ok := c.items.Get(id)
|
itemV, ok := c.items.Get(id)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -230,13 +230,13 @@ func (c *Collection) SetField(id, fieldName string, fieldValue float64) (
|
||||||
}
|
}
|
||||||
item := itemV
|
item := itemV
|
||||||
updated = c.setField(item, fieldName, fieldValue, true)
|
updated = c.setField(item, fieldName, fieldValue, true)
|
||||||
return item.Obj(), item.Fields(), updated, true
|
return item.Obj(), itemFields(item), updated, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFields is similar to SetField, just setting multiple fields at once
|
// SetFields is similar to SetField, just setting multiple fields at once
|
||||||
func (c *Collection) SetFields(
|
func (c *Collection) SetFields(
|
||||||
id string, fieldNames []string, fieldValues []float64,
|
id string, fieldNames []string, fieldValues []float64,
|
||||||
) (obj geojson.Object, fields []float64, updatedCount int, ok bool) {
|
) (obj geojson.Object, fields *Fields, updatedCount int, ok bool) {
|
||||||
itemV, ok := c.items.Get(id)
|
itemV, ok := c.items.Get(id)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, 0, false
|
return nil, nil, 0, false
|
||||||
|
@ -245,7 +245,7 @@ func (c *Collection) SetFields(
|
||||||
|
|
||||||
updatedCount = c.setFields(item, fieldNames, fieldValues, true)
|
updatedCount = c.setFields(item, fieldNames, fieldValues, true)
|
||||||
|
|
||||||
return item.Obj(), item.Fields(), updatedCount, true
|
return item.Obj(), itemFields(item), updatedCount, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// FieldMap return a maps of the field names.
|
// FieldMap return a maps of the field names.
|
||||||
|
@ -264,7 +264,7 @@ func (c *Collection) FieldArr() []string {
|
||||||
|
|
||||||
// Scan iterates though the collection ids.
|
// Scan iterates though the collection ids.
|
||||||
func (c *Collection) Scan(desc bool, cursor Cursor,
|
func (c *Collection) Scan(desc bool, cursor Cursor,
|
||||||
iterator func(id string, obj geojson.Object, fields []float64) bool,
|
iterator func(id string, obj geojson.Object, fields *Fields) bool,
|
||||||
) bool {
|
) bool {
|
||||||
var keepon = true
|
var keepon = true
|
||||||
var count uint64
|
var count uint64
|
||||||
|
@ -281,7 +281,7 @@ func (c *Collection) Scan(desc bool, cursor Cursor,
|
||||||
if cursor != nil {
|
if cursor != nil {
|
||||||
cursor.Step(1)
|
cursor.Step(1)
|
||||||
}
|
}
|
||||||
keepon = iterator(item.ID(), item.Obj(), item.Fields())
|
keepon = iterator(item.ID(), item.Obj(), itemFields(item))
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
if desc {
|
if desc {
|
||||||
|
@ -294,7 +294,7 @@ func (c *Collection) Scan(desc bool, cursor Cursor,
|
||||||
|
|
||||||
// ScanRange iterates though the collection starting with specified id.
|
// ScanRange iterates though the collection starting with specified id.
|
||||||
func (c *Collection) ScanRange(start, end string, desc bool, cursor Cursor,
|
func (c *Collection) ScanRange(start, end string, desc bool, cursor Cursor,
|
||||||
iterator func(id string, obj geojson.Object, fields []float64) bool,
|
iterator func(id string, obj geojson.Object, fields *Fields) bool,
|
||||||
) bool {
|
) bool {
|
||||||
var keepon = true
|
var keepon = true
|
||||||
var count uint64
|
var count uint64
|
||||||
|
@ -320,7 +320,7 @@ func (c *Collection) ScanRange(start, end string, desc bool, cursor Cursor,
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
keepon = iterator(item.ID(), item.Obj(), item.Fields())
|
keepon = iterator(item.ID(), item.Obj(), itemFields(item))
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,7 +334,7 @@ func (c *Collection) ScanRange(start, end string, desc bool, cursor Cursor,
|
||||||
|
|
||||||
// SearchValues iterates though the collection values.
|
// SearchValues iterates though the collection values.
|
||||||
func (c *Collection) SearchValues(desc bool, cursor Cursor,
|
func (c *Collection) SearchValues(desc bool, cursor Cursor,
|
||||||
iterator func(id string, obj geojson.Object, fields []float64) bool,
|
iterator func(id string, obj geojson.Object, fields *Fields) bool,
|
||||||
) bool {
|
) bool {
|
||||||
var keepon = true
|
var keepon = true
|
||||||
var count uint64
|
var count uint64
|
||||||
|
@ -352,7 +352,7 @@ func (c *Collection) SearchValues(desc bool, cursor Cursor,
|
||||||
cursor.Step(1)
|
cursor.Step(1)
|
||||||
}
|
}
|
||||||
iitm := v.(*item.Item)
|
iitm := v.(*item.Item)
|
||||||
keepon = iterator(iitm.ID(), iitm.Obj(), iitm.Fields())
|
keepon = iterator(iitm.ID(), iitm.Obj(), itemFields(iitm))
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
if desc {
|
if desc {
|
||||||
|
@ -366,7 +366,7 @@ func (c *Collection) SearchValues(desc bool, cursor Cursor,
|
||||||
// SearchValuesRange iterates though the collection values.
|
// SearchValuesRange iterates though the collection values.
|
||||||
func (c *Collection) SearchValuesRange(start, end string, desc bool,
|
func (c *Collection) SearchValuesRange(start, end string, desc bool,
|
||||||
cursor Cursor,
|
cursor Cursor,
|
||||||
iterator func(id string, obj geojson.Object, fields []float64) bool,
|
iterator func(id string, obj geojson.Object, fields *Fields) bool,
|
||||||
) bool {
|
) bool {
|
||||||
var keepon = true
|
var keepon = true
|
||||||
var count uint64
|
var count uint64
|
||||||
|
@ -384,7 +384,7 @@ func (c *Collection) SearchValuesRange(start, end string, desc bool,
|
||||||
cursor.Step(1)
|
cursor.Step(1)
|
||||||
}
|
}
|
||||||
iitm := v.(*item.Item)
|
iitm := v.(*item.Item)
|
||||||
keepon = iterator(iitm.ID(), iitm.Obj(), iitm.Fields())
|
keepon = iterator(iitm.ID(), iitm.Obj(), itemFields(iitm))
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
if desc {
|
if desc {
|
||||||
|
@ -402,7 +402,7 @@ func (c *Collection) SearchValuesRange(start, end string, desc bool,
|
||||||
// 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,
|
||||||
cursor Cursor,
|
cursor Cursor,
|
||||||
iterator func(id string, obj geojson.Object, fields []float64) bool,
|
iterator func(id string, obj geojson.Object, fields *Fields) bool,
|
||||||
) bool {
|
) bool {
|
||||||
var keepon = true
|
var keepon = true
|
||||||
var count uint64
|
var count uint64
|
||||||
|
@ -419,7 +419,7 @@ func (c *Collection) ScanGreaterOrEqual(id string, desc bool,
|
||||||
if cursor != nil {
|
if cursor != nil {
|
||||||
cursor.Step(1)
|
cursor.Step(1)
|
||||||
}
|
}
|
||||||
keepon = iterator(item.ID(), item.Obj(), item.Fields())
|
keepon = iterator(item.ID(), item.Obj(), itemFields(item))
|
||||||
return keepon
|
return keepon
|
||||||
}
|
}
|
||||||
if desc {
|
if desc {
|
||||||
|
@ -432,7 +432,7 @@ func (c *Collection) ScanGreaterOrEqual(id string, desc bool,
|
||||||
|
|
||||||
func (c *Collection) geoSearch(
|
func (c *Collection) geoSearch(
|
||||||
rect geometry.Rect,
|
rect geometry.Rect,
|
||||||
iter func(id string, obj geojson.Object, fields []float64) bool,
|
iter func(id string, obj geojson.Object, fields *Fields) bool,
|
||||||
) bool {
|
) bool {
|
||||||
alive := true
|
alive := true
|
||||||
c.index.Search(
|
c.index.Search(
|
||||||
|
@ -440,7 +440,7 @@ func (c *Collection) geoSearch(
|
||||||
[]float64{rect.Max.X, rect.Max.Y},
|
[]float64{rect.Max.X, rect.Max.Y},
|
||||||
func(_, _ []float64, itemv *item.Item) bool {
|
func(_, _ []float64, itemv *item.Item) bool {
|
||||||
item := itemv
|
item := itemv
|
||||||
alive = iter(item.ID(), item.Obj(), item.Fields())
|
alive = iter(item.ID(), item.Obj(), itemFields(item))
|
||||||
return alive
|
return alive
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -449,12 +449,12 @@ func (c *Collection) geoSearch(
|
||||||
|
|
||||||
func (c *Collection) geoSparse(
|
func (c *Collection) geoSparse(
|
||||||
obj geojson.Object, sparse uint8,
|
obj geojson.Object, sparse uint8,
|
||||||
iter func(id string, obj geojson.Object, fields []float64) (match, ok bool),
|
iter func(id string, obj geojson.Object, fields *Fields) (match, ok bool),
|
||||||
) bool {
|
) bool {
|
||||||
matches := make(map[string]bool)
|
matches := make(map[string]bool)
|
||||||
alive := true
|
alive := true
|
||||||
c.geoSparseInner(obj.Rect(), sparse,
|
c.geoSparseInner(obj.Rect(), sparse,
|
||||||
func(id string, o geojson.Object, fields []float64) (
|
func(id string, o geojson.Object, fields *Fields) (
|
||||||
match, ok bool,
|
match, ok bool,
|
||||||
) {
|
) {
|
||||||
ok = true
|
ok = true
|
||||||
|
@ -471,7 +471,7 @@ func (c *Collection) geoSparse(
|
||||||
}
|
}
|
||||||
func (c *Collection) geoSparseInner(
|
func (c *Collection) geoSparseInner(
|
||||||
rect geometry.Rect, sparse uint8,
|
rect geometry.Rect, sparse uint8,
|
||||||
iter func(id string, obj geojson.Object, fields []float64) (match, ok bool),
|
iter func(id string, obj geojson.Object, fields *Fields) (match, ok bool),
|
||||||
) bool {
|
) bool {
|
||||||
if sparse > 0 {
|
if sparse > 0 {
|
||||||
w := rect.Max.X - rect.Min.X
|
w := rect.Max.X - rect.Min.X
|
||||||
|
@ -503,7 +503,7 @@ func (c *Collection) geoSparseInner(
|
||||||
}
|
}
|
||||||
alive := true
|
alive := true
|
||||||
c.geoSearch(rect,
|
c.geoSearch(rect,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
match, ok := iter(id, obj, fields)
|
match, ok := iter(id, obj, fields)
|
||||||
if !ok {
|
if !ok {
|
||||||
alive = false
|
alive = false
|
||||||
|
@ -521,7 +521,7 @@ func (c *Collection) Within(
|
||||||
obj geojson.Object,
|
obj geojson.Object,
|
||||||
sparse uint8,
|
sparse uint8,
|
||||||
cursor Cursor,
|
cursor Cursor,
|
||||||
iter func(id string, obj geojson.Object, fields []float64) bool,
|
iter func(id string, obj geojson.Object, fields *Fields) bool,
|
||||||
) bool {
|
) bool {
|
||||||
var count uint64
|
var count uint64
|
||||||
var offset uint64
|
var offset uint64
|
||||||
|
@ -531,7 +531,7 @@ func (c *Collection) Within(
|
||||||
}
|
}
|
||||||
if sparse > 0 {
|
if sparse > 0 {
|
||||||
return c.geoSparse(obj, sparse,
|
return c.geoSparse(obj, sparse,
|
||||||
func(id string, o geojson.Object, fields []float64) (
|
func(id string, o geojson.Object, fields *Fields) (
|
||||||
match, ok bool,
|
match, ok bool,
|
||||||
) {
|
) {
|
||||||
count++
|
count++
|
||||||
|
@ -549,7 +549,7 @@ func (c *Collection) Within(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return c.geoSearch(obj.Rect(),
|
return c.geoSearch(obj.Rect(),
|
||||||
func(id string, o geojson.Object, fields []float64) bool {
|
func(id string, o geojson.Object, fields *Fields) bool {
|
||||||
count++
|
count++
|
||||||
if count <= offset {
|
if count <= offset {
|
||||||
return true
|
return true
|
||||||
|
@ -571,7 +571,7 @@ func (c *Collection) Intersects(
|
||||||
obj geojson.Object,
|
obj geojson.Object,
|
||||||
sparse uint8,
|
sparse uint8,
|
||||||
cursor Cursor,
|
cursor Cursor,
|
||||||
iter func(id string, obj geojson.Object, fields []float64) bool,
|
iter func(id string, obj geojson.Object, fields *Fields) bool,
|
||||||
) bool {
|
) bool {
|
||||||
var count uint64
|
var count uint64
|
||||||
var offset uint64
|
var offset uint64
|
||||||
|
@ -581,7 +581,7 @@ func (c *Collection) Intersects(
|
||||||
}
|
}
|
||||||
if sparse > 0 {
|
if sparse > 0 {
|
||||||
return c.geoSparse(obj, sparse,
|
return c.geoSparse(obj, sparse,
|
||||||
func(id string, o geojson.Object, fields []float64) (
|
func(id string, o geojson.Object, fields *Fields) (
|
||||||
match, ok bool,
|
match, ok bool,
|
||||||
) {
|
) {
|
||||||
count++
|
count++
|
||||||
|
@ -599,7 +599,7 @@ func (c *Collection) Intersects(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return c.geoSearch(obj.Rect(),
|
return c.geoSearch(obj.Rect(),
|
||||||
func(id string, o geojson.Object, fields []float64) bool {
|
func(id string, o geojson.Object, fields *Fields) bool {
|
||||||
count++
|
count++
|
||||||
if count <= offset {
|
if count <= offset {
|
||||||
return true
|
return true
|
||||||
|
@ -619,7 +619,7 @@ func (c *Collection) Intersects(
|
||||||
func (c *Collection) Nearby(
|
func (c *Collection) Nearby(
|
||||||
target geojson.Object,
|
target geojson.Object,
|
||||||
cursor Cursor,
|
cursor Cursor,
|
||||||
iter func(id string, obj geojson.Object, fields []float64) bool,
|
iter func(id string, obj geojson.Object, fields *Fields) bool,
|
||||||
) bool {
|
) bool {
|
||||||
// First look to see if there's at least one candidate in the circle's
|
// First look to see if there's at least one candidate in the circle's
|
||||||
// outer rectangle. This is a fast-fail operation.
|
// outer rectangle. This is a fast-fail operation.
|
||||||
|
@ -665,7 +665,7 @@ func (c *Collection) Nearby(
|
||||||
cursor.Step(1)
|
cursor.Step(1)
|
||||||
}
|
}
|
||||||
item := itemv
|
item := itemv
|
||||||
alive = iter(item.ID(), item.Obj(), item.Fields())
|
alive = iter(item.ID(), item.Obj(), itemFields(item))
|
||||||
return alive
|
return alive
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -104,7 +104,7 @@ func TestCollectionNewCollection(t *testing.T) {
|
||||||
Min: geometry.Point{X: -180, Y: -90},
|
Min: geometry.Point{X: -180, Y: -90},
|
||||||
Max: geometry.Point{X: 180, Y: 90},
|
Max: geometry.Point{X: 180, Y: 90},
|
||||||
}
|
}
|
||||||
c.geoSearch(bbox, func(id string, obj geojson.Object, field []float64) bool {
|
c.geoSearch(bbox, func(id string, obj geojson.Object, field *Fields) bool {
|
||||||
count++
|
count++
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
@ -124,8 +124,8 @@ func TestCollectionSet(t *testing.T) {
|
||||||
str1 := String("hello")
|
str1 := String("hello")
|
||||||
oldObject, oldFields, newFields := c.Set("str", str1, nil, nil)
|
oldObject, oldFields, newFields := c.Set("str", str1, nil, nil)
|
||||||
expect(t, oldObject == nil)
|
expect(t, oldObject == nil)
|
||||||
expect(t, len(oldFields) == 0)
|
expect(t, fieldLen(oldFields) == 0)
|
||||||
expect(t, len(newFields) == 0)
|
expect(t, fieldLen(newFields) == 0)
|
||||||
})
|
})
|
||||||
t.Run("UpdateString", func(t *testing.T) {
|
t.Run("UpdateString", func(t *testing.T) {
|
||||||
c := New()
|
c := New()
|
||||||
|
@ -133,20 +133,20 @@ func TestCollectionSet(t *testing.T) {
|
||||||
str2 := String("world")
|
str2 := String("world")
|
||||||
oldObject, oldFields, newFields := c.Set("str", str1, nil, nil)
|
oldObject, oldFields, newFields := c.Set("str", str1, nil, nil)
|
||||||
expect(t, oldObject == nil)
|
expect(t, oldObject == nil)
|
||||||
expect(t, len(oldFields) == 0)
|
expect(t, fieldLen(oldFields) == 0)
|
||||||
expect(t, len(newFields) == 0)
|
expect(t, fieldLen(newFields) == 0)
|
||||||
oldObject, oldFields, newFields = c.Set("str", str2, nil, nil)
|
oldObject, oldFields, newFields = c.Set("str", str2, nil, nil)
|
||||||
expect(t, oldObject == str1)
|
expect(t, oldObject == str1)
|
||||||
expect(t, len(oldFields) == 0)
|
expect(t, fieldLen(oldFields) == 0)
|
||||||
expect(t, len(newFields) == 0)
|
expect(t, fieldLen(newFields) == 0)
|
||||||
})
|
})
|
||||||
t.Run("AddPoint", func(t *testing.T) {
|
t.Run("AddPoint", func(t *testing.T) {
|
||||||
c := New()
|
c := New()
|
||||||
point1 := PO(-112.1, 33.1)
|
point1 := PO(-112.1, 33.1)
|
||||||
oldObject, oldFields, newFields := c.Set("point", point1, nil, nil)
|
oldObject, oldFields, newFields := c.Set("point", point1, nil, nil)
|
||||||
expect(t, oldObject == nil)
|
expect(t, oldObject == nil)
|
||||||
expect(t, len(oldFields) == 0)
|
expect(t, fieldLen(oldFields) == 0)
|
||||||
expect(t, len(newFields) == 0)
|
expect(t, fieldLen(newFields) == 0)
|
||||||
})
|
})
|
||||||
t.Run("UpdatePoint", func(t *testing.T) {
|
t.Run("UpdatePoint", func(t *testing.T) {
|
||||||
c := New()
|
c := New()
|
||||||
|
@ -154,12 +154,12 @@ func TestCollectionSet(t *testing.T) {
|
||||||
point2 := PO(-112.2, 33.2)
|
point2 := PO(-112.2, 33.2)
|
||||||
oldObject, oldFields, newFields := c.Set("point", point1, nil, nil)
|
oldObject, oldFields, newFields := c.Set("point", point1, nil, nil)
|
||||||
expect(t, oldObject == nil)
|
expect(t, oldObject == nil)
|
||||||
expect(t, len(oldFields) == 0)
|
expect(t, fieldLen(oldFields) == 0)
|
||||||
expect(t, len(newFields) == 0)
|
expect(t, fieldLen(newFields) == 0)
|
||||||
oldObject, oldFields, newFields = c.Set("point", point2, nil, nil)
|
oldObject, oldFields, newFields = c.Set("point", point2, nil, nil)
|
||||||
expect(t, oldObject == point1)
|
expect(t, oldObject == point1)
|
||||||
expect(t, len(oldFields) == 0)
|
expect(t, fieldLen(oldFields) == 0)
|
||||||
expect(t, len(newFields) == 0)
|
expect(t, fieldLen(newFields) == 0)
|
||||||
})
|
})
|
||||||
t.Run("Fields", func(t *testing.T) {
|
t.Run("Fields", func(t *testing.T) {
|
||||||
c := New()
|
c := New()
|
||||||
|
@ -170,23 +170,23 @@ func TestCollectionSet(t *testing.T) {
|
||||||
fValues := []float64{1, 2, 3}
|
fValues := []float64{1, 2, 3}
|
||||||
oldObj, oldFlds, newFlds := c.Set("str", str1, fNames, fValues)
|
oldObj, oldFlds, newFlds := c.Set("str", str1, fNames, fValues)
|
||||||
expect(t, oldObj == nil)
|
expect(t, oldObj == nil)
|
||||||
expect(t, len(oldFlds) == 0)
|
expect(t, fieldLen(oldFlds) == 0)
|
||||||
expect(t, reflect.DeepEqual(newFlds, fValues))
|
expect(t, fieldIterEquals(newFlds, fValues))
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
fNames := []string{"d", "e", "f"}
|
fNames := []string{"d", "e", "f"}
|
||||||
fValues := []float64{4, 5, 6}
|
fValues := []float64{4, 5, 6}
|
||||||
oldObj, oldFlds, newFlds := c.Set("str", str2, fNames, fValues)
|
oldObj, oldFlds, newFlds := c.Set("str", str2, fNames, fValues)
|
||||||
expect(t, oldObj == str1)
|
expect(t, oldObj == str1)
|
||||||
expect(t, reflect.DeepEqual(oldFlds, []float64{1, 2, 3}))
|
expect(t, fieldIterEquals(oldFlds, []float64{1, 2, 3}))
|
||||||
expect(t, reflect.DeepEqual(newFlds, []float64{1, 2, 3, 4, 5, 6}))
|
expect(t, fieldIterEquals(newFlds, []float64{1, 2, 3, 4, 5, 6}))
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
fValues := []float64{7, 8, 9, 10, 11, 12}
|
fValues := []float64{7, 8, 9, 10, 11, 12}
|
||||||
oldObj, oldFlds, newFlds := c.Set("str", str1, nil, fValues)
|
oldObj, oldFlds, newFlds := c.Set("str", str1, nil, fValues)
|
||||||
expect(t, oldObj == str2)
|
expect(t, oldObj == str2)
|
||||||
expect(t, reflect.DeepEqual(oldFlds, []float64{1, 2, 3, 4, 5, 6}))
|
expect(t, fieldIterEquals(oldFlds, []float64{1, 2, 3, 4, 5, 6}))
|
||||||
expect(t, reflect.DeepEqual(newFlds, []float64{7, 8, 9, 10, 11, 12}))
|
expect(t, fieldIterEquals(newFlds, []float64{7, 8, 9, 10, 11, 12}))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
t.Run("Delete", func(t *testing.T) {
|
t.Run("Delete", func(t *testing.T) {
|
||||||
|
@ -204,7 +204,7 @@ func TestCollectionSet(t *testing.T) {
|
||||||
Max: geometry.Point{X: 1, Y: 2}})
|
Max: geometry.Point{X: 1, Y: 2}})
|
||||||
var v geojson.Object
|
var v geojson.Object
|
||||||
var ok bool
|
var ok bool
|
||||||
var flds []float64
|
var flds *Fields
|
||||||
var updated bool
|
var updated bool
|
||||||
var updateCount int
|
var updateCount int
|
||||||
|
|
||||||
|
@ -226,24 +226,24 @@ func TestCollectionSet(t *testing.T) {
|
||||||
|
|
||||||
v, flds, updated, ok = c.SetField("3", "hello", 123)
|
v, flds, updated, ok = c.SetField("3", "hello", 123)
|
||||||
expect(t, ok)
|
expect(t, ok)
|
||||||
expect(t, reflect.DeepEqual(flds, []float64{123}))
|
expect(t, fieldIterEquals(flds, []float64{123}))
|
||||||
expect(t, updated)
|
expect(t, updated)
|
||||||
expect(t, c.FieldMap()["hello"] == 0)
|
expect(t, c.FieldMap()["hello"] == 0)
|
||||||
|
|
||||||
v, flds, updated, ok = c.SetField("3", "hello", 1234)
|
v, flds, updated, ok = c.SetField("3", "hello", 1234)
|
||||||
expect(t, ok)
|
expect(t, ok)
|
||||||
expect(t, reflect.DeepEqual(flds, []float64{1234}))
|
expect(t, fieldIterEquals(flds, []float64{1234}))
|
||||||
expect(t, updated)
|
expect(t, updated)
|
||||||
|
|
||||||
v, flds, updated, ok = c.SetField("3", "hello", 1234)
|
v, flds, updated, ok = c.SetField("3", "hello", 1234)
|
||||||
expect(t, ok)
|
expect(t, ok)
|
||||||
expect(t, reflect.DeepEqual(flds, []float64{1234}))
|
expect(t, fieldIterEquals(flds, []float64{1234}))
|
||||||
expect(t, !updated)
|
expect(t, !updated)
|
||||||
|
|
||||||
v, flds, updateCount, ok = c.SetFields("3",
|
v, flds, updateCount, ok = c.SetFields("3",
|
||||||
[]string{"planet", "world"}, []float64{55, 66})
|
[]string{"planet", "world"}, []float64{55, 66})
|
||||||
expect(t, ok)
|
expect(t, ok)
|
||||||
expect(t, reflect.DeepEqual(flds, []float64{1234, 55, 66}))
|
expect(t, fieldIterEquals(flds, []float64{1234, 55, 66}))
|
||||||
expect(t, updateCount == 2)
|
expect(t, updateCount == 2)
|
||||||
expect(t, c.FieldMap()["hello"] == 0)
|
expect(t, c.FieldMap()["hello"] == 0)
|
||||||
expect(t, c.FieldMap()["planet"] == 1)
|
expect(t, c.FieldMap()["planet"] == 1)
|
||||||
|
@ -277,6 +277,29 @@ func TestCollectionSet(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fieldLen(fields *Fields) int {
|
||||||
|
var idx int
|
||||||
|
fields.ForEach(-1, func(value float64) bool {
|
||||||
|
idx++
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
|
||||||
|
func fieldIterEquals(fields *Fields, values []float64) bool {
|
||||||
|
ok := true
|
||||||
|
var idx int
|
||||||
|
fields.ForEach(len(values), func(value float64) bool {
|
||||||
|
if value != values[idx] {
|
||||||
|
ok = false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
idx++
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
func TestCollectionScan(t *testing.T) {
|
func TestCollectionScan(t *testing.T) {
|
||||||
N := 256
|
N := 256
|
||||||
c := New()
|
c := New()
|
||||||
|
@ -286,22 +309,22 @@ func TestCollectionScan(t *testing.T) {
|
||||||
}
|
}
|
||||||
var n int
|
var n int
|
||||||
var prevID string
|
var prevID string
|
||||||
c.Scan(false, nil, func(id string, obj geojson.Object, fields []float64) bool {
|
c.Scan(false, nil, func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
expect(t, id > prevID)
|
expect(t, id > prevID)
|
||||||
}
|
}
|
||||||
expect(t, id == fmt.Sprintf("%04d", int(fields[0])))
|
expect(t, id == fmt.Sprintf("%04d", int(fields.Get(0))))
|
||||||
n++
|
n++
|
||||||
prevID = id
|
prevID = id
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
expect(t, n == c.Count())
|
expect(t, n == c.Count())
|
||||||
n = 0
|
n = 0
|
||||||
c.Scan(true, nil, func(id string, obj geojson.Object, fields []float64) bool {
|
c.Scan(true, nil, func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
expect(t, id < prevID)
|
expect(t, id < prevID)
|
||||||
}
|
}
|
||||||
expect(t, id == fmt.Sprintf("%04d", int(fields[0])))
|
expect(t, id == fmt.Sprintf("%04d", int(fields.Get(0))))
|
||||||
n++
|
n++
|
||||||
prevID = id
|
prevID = id
|
||||||
return true
|
return true
|
||||||
|
@ -310,11 +333,11 @@ func TestCollectionScan(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.ScanRange("0060", "0070", false, nil,
|
c.ScanRange("0060", "0070", false, nil,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
expect(t, id > prevID)
|
expect(t, id > prevID)
|
||||||
}
|
}
|
||||||
expect(t, id == fmt.Sprintf("%04d", int(fields[0])))
|
expect(t, id == fmt.Sprintf("%04d", int(fields.Get(0))))
|
||||||
n++
|
n++
|
||||||
prevID = id
|
prevID = id
|
||||||
return true
|
return true
|
||||||
|
@ -323,11 +346,11 @@ func TestCollectionScan(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.ScanRange("0070", "0060", true, nil,
|
c.ScanRange("0070", "0060", true, nil,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
expect(t, id < prevID)
|
expect(t, id < prevID)
|
||||||
}
|
}
|
||||||
expect(t, id == fmt.Sprintf("%04d", int(fields[0])))
|
expect(t, id == fmt.Sprintf("%04d", int(fields.Get(0))))
|
||||||
n++
|
n++
|
||||||
prevID = id
|
prevID = id
|
||||||
return true
|
return true
|
||||||
|
@ -336,11 +359,11 @@ func TestCollectionScan(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.ScanGreaterOrEqual("0070", true, nil,
|
c.ScanGreaterOrEqual("0070", true, nil,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
expect(t, id < prevID)
|
expect(t, id < prevID)
|
||||||
}
|
}
|
||||||
expect(t, id == fmt.Sprintf("%04d", int(fields[0])))
|
expect(t, id == fmt.Sprintf("%04d", int(fields.Get(0))))
|
||||||
n++
|
n++
|
||||||
prevID = id
|
prevID = id
|
||||||
return true
|
return true
|
||||||
|
@ -349,11 +372,11 @@ func TestCollectionScan(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.ScanGreaterOrEqual("0070", false, nil,
|
c.ScanGreaterOrEqual("0070", false, nil,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
expect(t, id > prevID)
|
expect(t, id > prevID)
|
||||||
}
|
}
|
||||||
expect(t, id == fmt.Sprintf("%04d", int(fields[0])))
|
expect(t, id == fmt.Sprintf("%04d", int(fields.Get(0))))
|
||||||
n++
|
n++
|
||||||
prevID = id
|
prevID = id
|
||||||
return true
|
return true
|
||||||
|
@ -373,22 +396,22 @@ func TestCollectionSearch(t *testing.T) {
|
||||||
}
|
}
|
||||||
var n int
|
var n int
|
||||||
var prevValue string
|
var prevValue string
|
||||||
c.SearchValues(false, nil, func(id string, obj geojson.Object, fields []float64) bool {
|
c.SearchValues(false, nil, func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
expect(t, obj.String() > prevValue)
|
expect(t, obj.String() > prevValue)
|
||||||
}
|
}
|
||||||
expect(t, id == fmt.Sprintf("%04d", int(fields[1])))
|
expect(t, id == fmt.Sprintf("%04d", int(fields.Get(1))))
|
||||||
n++
|
n++
|
||||||
prevValue = obj.String()
|
prevValue = obj.String()
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
expect(t, n == c.Count())
|
expect(t, n == c.Count())
|
||||||
n = 0
|
n = 0
|
||||||
c.SearchValues(true, nil, func(id string, obj geojson.Object, fields []float64) bool {
|
c.SearchValues(true, nil, func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
expect(t, obj.String() < prevValue)
|
expect(t, obj.String() < prevValue)
|
||||||
}
|
}
|
||||||
expect(t, id == fmt.Sprintf("%04d", int(fields[1])))
|
expect(t, id == fmt.Sprintf("%04d", int(fields.Get(1))))
|
||||||
n++
|
n++
|
||||||
prevValue = obj.String()
|
prevValue = obj.String()
|
||||||
return true
|
return true
|
||||||
|
@ -397,11 +420,11 @@ func TestCollectionSearch(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.SearchValuesRange("0060", "0070", false, nil,
|
c.SearchValuesRange("0060", "0070", false, nil,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
expect(t, obj.String() > prevValue)
|
expect(t, obj.String() > prevValue)
|
||||||
}
|
}
|
||||||
expect(t, id == fmt.Sprintf("%04d", int(fields[1])))
|
expect(t, id == fmt.Sprintf("%04d", int(fields.Get(1))))
|
||||||
n++
|
n++
|
||||||
prevValue = obj.String()
|
prevValue = obj.String()
|
||||||
return true
|
return true
|
||||||
|
@ -410,11 +433,11 @@ func TestCollectionSearch(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.SearchValuesRange("0070", "0060", true, nil,
|
c.SearchValuesRange("0070", "0060", true, nil,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
expect(t, obj.String() < prevValue)
|
expect(t, obj.String() < prevValue)
|
||||||
}
|
}
|
||||||
expect(t, id == fmt.Sprintf("%04d", int(fields[1])))
|
expect(t, id == fmt.Sprintf("%04d", int(fields.Get(1))))
|
||||||
n++
|
n++
|
||||||
prevValue = obj.String()
|
prevValue = obj.String()
|
||||||
return true
|
return true
|
||||||
|
@ -493,7 +516,7 @@ func TestSpatialSearch(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.Within(q1, 0, nil,
|
c.Within(q1, 0, nil,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
n++
|
n++
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
@ -502,7 +525,7 @@ func TestSpatialSearch(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.Within(q2, 0, nil,
|
c.Within(q2, 0, nil,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
n++
|
n++
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
@ -511,7 +534,7 @@ func TestSpatialSearch(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.Within(q3, 0, nil,
|
c.Within(q3, 0, nil,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
n++
|
n++
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
@ -520,7 +543,7 @@ func TestSpatialSearch(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.Intersects(q1, 0, nil,
|
c.Intersects(q1, 0, nil,
|
||||||
func(_ string, _ geojson.Object, _ []float64) bool {
|
func(_ string, _ geojson.Object, _ *Fields) bool {
|
||||||
n++
|
n++
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
@ -529,7 +552,7 @@ func TestSpatialSearch(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.Intersects(q2, 0, nil,
|
c.Intersects(q2, 0, nil,
|
||||||
func(_ string, _ geojson.Object, _ []float64) bool {
|
func(_ string, _ geojson.Object, _ *Fields) bool {
|
||||||
n++
|
n++
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
@ -538,7 +561,7 @@ func TestSpatialSearch(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.Intersects(q3, 0, nil,
|
c.Intersects(q3, 0, nil,
|
||||||
func(_ string, _ geojson.Object, _ []float64) bool {
|
func(_ string, _ geojson.Object, _ *Fields) bool {
|
||||||
n++
|
n++
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
@ -547,7 +570,7 @@ func TestSpatialSearch(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.Intersects(q3, 0, nil,
|
c.Intersects(q3, 0, nil,
|
||||||
func(_ string, _ geojson.Object, _ []float64) bool {
|
func(_ string, _ geojson.Object, _ *Fields) bool {
|
||||||
n++
|
n++
|
||||||
return n <= 1
|
return n <= 1
|
||||||
},
|
},
|
||||||
|
@ -559,7 +582,7 @@ func TestSpatialSearch(t *testing.T) {
|
||||||
r2, p1, p4, r1, p3, r3, p2,
|
r2, p1, p4, r1, p3, r3, p2,
|
||||||
}
|
}
|
||||||
c.Nearby(q4, nil,
|
c.Nearby(q4, nil,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
items = append(items, obj)
|
items = append(items, obj)
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
@ -585,7 +608,7 @@ func TestCollectionSparse(t *testing.T) {
|
||||||
var n int
|
var n int
|
||||||
n = 0
|
n = 0
|
||||||
c.Within(rect, 1, nil,
|
c.Within(rect, 1, nil,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
n++
|
n++
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
@ -594,7 +617,7 @@ func TestCollectionSparse(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.Within(rect, 2, nil,
|
c.Within(rect, 2, nil,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
n++
|
n++
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
@ -603,7 +626,7 @@ func TestCollectionSparse(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.Within(rect, 3, nil,
|
c.Within(rect, 3, nil,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
n++
|
n++
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
@ -612,7 +635,7 @@ func TestCollectionSparse(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.Within(rect, 3, nil,
|
c.Within(rect, 3, nil,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
n++
|
n++
|
||||||
return n <= 30
|
return n <= 30
|
||||||
},
|
},
|
||||||
|
@ -621,7 +644,7 @@ func TestCollectionSparse(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.Intersects(rect, 3, nil,
|
c.Intersects(rect, 3, nil,
|
||||||
func(id string, _ geojson.Object, _ []float64) bool {
|
func(id string, _ geojson.Object, _ *Fields) bool {
|
||||||
n++
|
n++
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
@ -630,7 +653,7 @@ func TestCollectionSparse(t *testing.T) {
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
c.Intersects(rect, 3, nil,
|
c.Intersects(rect, 3, nil,
|
||||||
func(id string, _ geojson.Object, _ []float64) bool {
|
func(id string, _ geojson.Object, _ *Fields) bool {
|
||||||
n++
|
n++
|
||||||
return n <= 30
|
return n <= 30
|
||||||
},
|
},
|
||||||
|
@ -683,7 +706,7 @@ func TestManyCollections(t *testing.T) {
|
||||||
Min: geometry.Point{X: -180, Y: 30},
|
Min: geometry.Point{X: -180, Y: 30},
|
||||||
Max: geometry.Point{X: 34, Y: 100},
|
Max: geometry.Point{X: 34, Y: 100},
|
||||||
}
|
}
|
||||||
col.geoSearch(bbox, func(id string, obj geojson.Object, fields []float64) bool {
|
col.geoSearch(bbox, func(id string, obj geojson.Object, fields *Fields) bool {
|
||||||
//println(id)
|
//println(id)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
import "github.com/tidwall/tile38/internal/collection/item"
|
||||||
|
|
||||||
|
// // FieldIter ...
|
||||||
|
// type FieldIter interface {
|
||||||
|
// ForEachField(count int, iter func(value float64) bool)
|
||||||
|
// GetField(index int) float64
|
||||||
|
// HasFields() bool
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Fields ...
|
||||||
|
type Fields struct {
|
||||||
|
item *item.Item
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForEach iterates over each field. The count param is the number of
|
||||||
|
// iterations. When count is less than zero, then all fields are returns.
|
||||||
|
func (fields *Fields) ForEach(count int, iter func(value float64) bool) {
|
||||||
|
if fields == nil || fields.item == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fields.item.ForEachField(count, iter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the value for a field at index. If there is no field at index,
|
||||||
|
// then zero is returned.
|
||||||
|
func (fields *Fields) Get(index int) float64 {
|
||||||
|
if fields == nil || fields.item == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return fields.item.GetField(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func itemFields(item *item.Item) *Fields {
|
||||||
|
if item == nil || !item.HasFields() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &Fields{item: item}
|
||||||
|
}
|
|
@ -39,7 +39,7 @@ func (item *Item) ID() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields returns the field values
|
// Fields returns the field values
|
||||||
func (item *Item) Fields() []float64 {
|
func (item *Item) fields() []float64 {
|
||||||
return *(*[]float64)((unsafe.Pointer)(&reflect.SliceHeader{
|
return *(*[]float64)((unsafe.Pointer)(&reflect.SliceHeader{
|
||||||
Data: uintptr(unsafe.Pointer(item.data)),
|
Data: uintptr(unsafe.Pointer(item.data)),
|
||||||
Len: int(item.fieldsLen) / 8,
|
Len: int(item.fieldsLen) / 8,
|
||||||
|
@ -104,8 +104,15 @@ func (item *Item) Less(other btree.Item, ctx interface{}) bool {
|
||||||
return item.ID() < other.(*Item).ID()
|
return item.ID() < other.(*Item).ID()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyOverFields overwriting previous fields
|
// CopyOverFields overwriting previous fields. Accepts an *Item or []float64
|
||||||
func (item *Item) CopyOverFields(values []float64) {
|
func (item *Item) CopyOverFields(from interface{}) {
|
||||||
|
var values []float64
|
||||||
|
switch from := from.(type) {
|
||||||
|
case *Item:
|
||||||
|
values = from.fields()
|
||||||
|
case []float64:
|
||||||
|
values = from
|
||||||
|
}
|
||||||
fieldBytes := floatsToBytes(values)
|
fieldBytes := floatsToBytes(values)
|
||||||
oldData := item.dataBytes()
|
oldData := item.dataBytes()
|
||||||
newData := make([]byte, len(fieldBytes)+int(item.idLen))
|
newData := make([]byte, len(fieldBytes)+int(item.idLen))
|
||||||
|
@ -153,15 +160,6 @@ func (item *Item) SetField(index int, value float64) (updated bool) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetField returns the value for a field at index.
|
|
||||||
func (item *Item) GetField(index int) float64 {
|
|
||||||
numFields := int(item.fieldsLen / 8)
|
|
||||||
if index < numFields {
|
|
||||||
return getFieldAt(item.data, index)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (item *Item) dataBytes() []byte {
|
func (item *Item) dataBytes() []byte {
|
||||||
return *(*[]byte)((unsafe.Pointer)(&reflect.SliceHeader{
|
return *(*[]byte)((unsafe.Pointer)(&reflect.SliceHeader{
|
||||||
Data: uintptr(unsafe.Pointer(item.data)),
|
Data: uintptr(unsafe.Pointer(item.data)),
|
||||||
|
@ -177,3 +175,44 @@ func floatsToBytes(f []float64) []byte {
|
||||||
Cap: len(f) * 8,
|
Cap: len(f) * 8,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForEachField iterates over each field. The count param is the number of
|
||||||
|
// iterations. When count is less than zero, then all fields are returns.
|
||||||
|
func (item *Item) ForEachField(count int, iter func(value float64) bool) {
|
||||||
|
if item == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fields := item.fields()
|
||||||
|
var n int
|
||||||
|
if count < 0 {
|
||||||
|
n = len(fields)
|
||||||
|
} else {
|
||||||
|
n = count
|
||||||
|
}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
var field float64
|
||||||
|
if i < len(fields) {
|
||||||
|
field = fields[i]
|
||||||
|
}
|
||||||
|
if !iter(field) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetField returns the value for a field at index.
|
||||||
|
func (item *Item) GetField(index int) float64 {
|
||||||
|
if item == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
numFields := int(item.fieldsLen / 8)
|
||||||
|
if index < numFields {
|
||||||
|
return getFieldAt(item.data, index)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasFields returns true when item has fields
|
||||||
|
func (item *Item) HasFields() bool {
|
||||||
|
return item != nil && item.fieldsLen > 0
|
||||||
|
}
|
||||||
|
|
|
@ -19,7 +19,12 @@ func testRandItem(t *testing.T) {
|
||||||
for i := range values {
|
for i := range values {
|
||||||
values[i] = rand.Float64()
|
values[i] = rand.Float64()
|
||||||
}
|
}
|
||||||
item := New(key, geojson.NewPoint(geometry.Point{X: 1, Y: 2}))
|
var item *Item
|
||||||
|
if rand.Int()%2 == 0 {
|
||||||
|
item = New(key, geojson.NewSimplePoint(geometry.Point{X: 1, Y: 2}))
|
||||||
|
} else {
|
||||||
|
item = New(key, geojson.NewPoint(geometry.Point{X: 1, Y: 2}))
|
||||||
|
}
|
||||||
if item.ID() != key {
|
if item.ID() != key {
|
||||||
t.Fatalf("expected '%v', got '%v'", key, item.ID())
|
t.Fatalf("expected '%v', got '%v'", key, item.ID())
|
||||||
}
|
}
|
||||||
|
@ -38,7 +43,7 @@ func testRandItem(t *testing.T) {
|
||||||
t.Fatalf("expected '%v', got '%v'", values[i], item.GetField(i))
|
t.Fatalf("expected '%v', got '%v'", values[i], item.GetField(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fields := item.Fields()
|
fields := item.fields()
|
||||||
for i := 0; i < len(fields); i++ {
|
for i := 0; i < len(fields); i++ {
|
||||||
for _, j := range setValues {
|
for _, j := range setValues {
|
||||||
if i == j {
|
if i == j {
|
||||||
|
@ -65,6 +70,42 @@ func testRandItem(t *testing.T) {
|
||||||
t.Fatal("expected false")
|
t.Fatal("expected false")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var fvalues []float64
|
||||||
|
item.ForEachField(len(values), func(value float64) bool {
|
||||||
|
fvalues = append(fvalues, value)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if !reflect.DeepEqual(values, fvalues) {
|
||||||
|
t.Fatalf("expected '%v', got '%v'", values, fvalues)
|
||||||
|
}
|
||||||
|
|
||||||
|
fvalues = nil
|
||||||
|
item.ForEachField(len(values), func(value float64) bool {
|
||||||
|
if len(fvalues) == 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
fvalues = append(fvalues, value)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if len(values) > 0 && len(fvalues) != 1 {
|
||||||
|
t.Fatalf("expected '%v', got '%v'", 1, len(fvalues))
|
||||||
|
}
|
||||||
|
|
||||||
|
fvalues = nil
|
||||||
|
item.ForEachField(-1, func(value float64) bool {
|
||||||
|
fvalues = append(fvalues, value)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if !reflect.DeepEqual(values, fvalues) {
|
||||||
|
t.Fatalf("expected '%v', got '%v'", 1, len(fvalues))
|
||||||
|
}
|
||||||
|
|
||||||
|
// should not fail, must allow nil receiver
|
||||||
|
(*Item)(nil).ForEachField(1, nil)
|
||||||
|
if (*Item)(nil).GetField(1) != 0 {
|
||||||
|
t.Fatalf("expected '%v', got '%v'", 0, (*Item)(nil).GetField(1))
|
||||||
|
}
|
||||||
|
|
||||||
if item.ID() != key {
|
if item.ID() != key {
|
||||||
t.Fatalf("expected '%v', got '%v'", key, item.ID())
|
t.Fatalf("expected '%v', got '%v'", key, item.ID())
|
||||||
}
|
}
|
||||||
|
@ -79,8 +120,22 @@ func testRandItem(t *testing.T) {
|
||||||
if points != 1 {
|
if points != 1 {
|
||||||
t.Fatalf("expected '%v', got '%v'", 1, points)
|
t.Fatalf("expected '%v', got '%v'", 1, points)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(item.Fields(), values) {
|
if !reflect.DeepEqual(item.fields(), values) {
|
||||||
t.Fatalf("expected '%v', got '%v'", values, item.Fields())
|
t.Fatalf("expected '%v', got '%v'", values, item.fields())
|
||||||
|
}
|
||||||
|
item.CopyOverFields(item)
|
||||||
|
weight, points = item.WeightAndPoints()
|
||||||
|
if weight != len(values)*8+len(key)+points*16 {
|
||||||
|
t.Fatalf("expected '%v', got '%v'", len(values)*8+len(key)+points*16, weight)
|
||||||
|
}
|
||||||
|
if points != 1 {
|
||||||
|
t.Fatalf("expected '%v', got '%v'", 1, points)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(item.fields(), values) {
|
||||||
|
t.Fatalf("expected '%v', got '%v'", values, item.fields())
|
||||||
|
}
|
||||||
|
if !item.HasFields() {
|
||||||
|
t.Fatal("expected true")
|
||||||
}
|
}
|
||||||
|
|
||||||
item.CopyOverFields(nil)
|
item.CopyOverFields(nil)
|
||||||
|
@ -91,12 +146,16 @@ func testRandItem(t *testing.T) {
|
||||||
if points != 1 {
|
if points != 1 {
|
||||||
t.Fatalf("expected '%v', got '%v'", 1, points)
|
t.Fatalf("expected '%v', got '%v'", 1, points)
|
||||||
}
|
}
|
||||||
if len(item.Fields()) != 0 {
|
if len(item.fields()) != 0 {
|
||||||
t.Fatalf("expected '%#v', got '%#v'", 0, len(item.Fields()))
|
t.Fatalf("expected '%#v', got '%#v'", 0, len(item.fields()))
|
||||||
}
|
}
|
||||||
if item.ID() != key {
|
if item.ID() != key {
|
||||||
t.Fatalf("expected '%v', got '%v'", key, item.ID())
|
t.Fatalf("expected '%v', got '%v'", key, item.ID())
|
||||||
}
|
}
|
||||||
|
if item.HasFields() {
|
||||||
|
t.Fatal("expected false")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestItem(t *testing.T) {
|
func TestItem(t *testing.T) {
|
||||||
|
|
|
@ -96,8 +96,9 @@ func (server *Server) aofshrink() {
|
||||||
var exm = server.expires[keys[0]] // the expiration map
|
var exm = server.expires[keys[0]] // the expiration map
|
||||||
var now = time.Now() // used for expiration
|
var now = time.Now() // used for expiration
|
||||||
var count = 0 // the object count
|
var count = 0 // the object count
|
||||||
|
|
||||||
col.ScanGreaterOrEqual(nextid, false, nil,
|
col.ScanGreaterOrEqual(nextid, false, nil,
|
||||||
func(id string, obj geojson.Object, fields []float64) bool {
|
func(id string, obj geojson.Object, fields *collection.Fields) bool {
|
||||||
if count == maxids {
|
if count == maxids {
|
||||||
// we reached the max number of ids for one batch
|
// we reached the max number of ids for one batch
|
||||||
nextid = id
|
nextid = id
|
||||||
|
@ -110,13 +111,16 @@ func (server *Server) aofshrink() {
|
||||||
values = append(values, "set")
|
values = append(values, "set")
|
||||||
values = append(values, keys[0])
|
values = append(values, keys[0])
|
||||||
values = append(values, id)
|
values = append(values, id)
|
||||||
for i, fvalue := range fields {
|
var fidx int
|
||||||
|
fields.ForEach(len(fnames), func(fvalue float64) bool {
|
||||||
if fvalue != 0 {
|
if fvalue != 0 {
|
||||||
values = append(values, "field")
|
values = append(values, "field")
|
||||||
values = append(values, fnames[i])
|
values = append(values, fnames[fidx])
|
||||||
values = append(values, strconv.FormatFloat(fvalue, 'f', -1, 64))
|
values = append(values, strconv.FormatFloat(fvalue, 'f', -1, 64))
|
||||||
}
|
}
|
||||||
}
|
fidx++
|
||||||
|
return true
|
||||||
|
})
|
||||||
if exm != nil {
|
if exm != nil {
|
||||||
at, ok := exm[id]
|
at, ok := exm[id]
|
||||||
if ok {
|
if ok {
|
||||||
|
|
|
@ -34,18 +34,16 @@ func (a byField) Swap(i, j int) {
|
||||||
a[i], a[j] = a[j], a[i]
|
a[i], a[j] = a[j], a[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
func orderFields(fmap map[string]int, fields []float64) []fvt {
|
func orderFields(fmap map[string]int, fields *collection.Fields) []fvt {
|
||||||
var fv fvt
|
var fv fvt
|
||||||
fvs := make([]fvt, 0, len(fmap))
|
fvs := make([]fvt, 0, len(fmap))
|
||||||
for field, idx := range fmap {
|
for field, idx := range fmap {
|
||||||
if idx < len(fields) {
|
|
||||||
fv.field = field
|
fv.field = field
|
||||||
fv.value = fields[idx]
|
fv.value = fields.Get(idx)
|
||||||
if fv.value != 0 {
|
if fv.value != 0 {
|
||||||
fvs = append(fvs, fv)
|
fvs = append(fvs, fv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
sort.Sort(byField(fvs))
|
sort.Sort(byField(fvs))
|
||||||
return fvs
|
return fvs
|
||||||
}
|
}
|
||||||
|
@ -352,7 +350,7 @@ func (server *Server) cmdPdel(msg *Message) (res resp.Value, d commandDetails, e
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
iter := func(id string, o geojson.Object, fields []float64) bool {
|
iter := func(id string, o geojson.Object, fields *collection.Fields) bool {
|
||||||
if match, _ := glob.Match(d.pattern, id); match {
|
if match, _ := glob.Match(d.pattern, id); match {
|
||||||
d.children = append(d.children, &commandDetails{
|
d.children = append(d.children, &commandDetails{
|
||||||
command: "del",
|
command: "del",
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/tidwall/geojson/geo"
|
"github.com/tidwall/geojson/geo"
|
||||||
"github.com/tidwall/geojson/geometry"
|
"github.com/tidwall/geojson/geometry"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
|
"github.com/tidwall/tile38/internal/collection"
|
||||||
"github.com/tidwall/tile38/internal/glob"
|
"github.com/tidwall/tile38/internal/glob"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -284,7 +285,7 @@ func extendRoamMessage(
|
||||||
}
|
}
|
||||||
pattern := match.id + fence.roam.scan
|
pattern := match.id + fence.roam.scan
|
||||||
iterator := func(
|
iterator := func(
|
||||||
oid string, o geojson.Object, fields []float64,
|
oid string, o geojson.Object, fields *collection.Fields,
|
||||||
) bool {
|
) bool {
|
||||||
if oid == match.id {
|
if oid == match.id {
|
||||||
return true
|
return true
|
||||||
|
@ -371,7 +372,7 @@ func fenceMatchRoam(
|
||||||
Max: geometry.Point{X: maxLon, Y: maxLat},
|
Max: geometry.Point{X: maxLon, Y: maxLat},
|
||||||
}
|
}
|
||||||
col.Intersects(geojson.NewRect(rect), 0, nil, func(
|
col.Intersects(geojson.NewRect(rect), 0, nil, func(
|
||||||
id string, obj2 geojson.Object, fields []float64,
|
id string, obj2 geojson.Object, fields *collection.Fields,
|
||||||
) bool {
|
) bool {
|
||||||
if c.hasExpired(fence.roam.key, id) {
|
if c.hasExpired(fence.roam.key, id) {
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/tidwall/geojson"
|
"github.com/tidwall/geojson"
|
||||||
"github.com/tidwall/resp"
|
"github.com/tidwall/resp"
|
||||||
|
"github.com/tidwall/tile38/internal/collection"
|
||||||
"github.com/tidwall/tile38/internal/glob"
|
"github.com/tidwall/tile38/internal/glob"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ func (c *Server) cmdScan(msg *Message) (res resp.Value, err error) {
|
||||||
g := glob.Parse(sw.globPattern, s.desc)
|
g := glob.Parse(sw.globPattern, s.desc)
|
||||||
if g.Limits[0] == "" && g.Limits[1] == "" {
|
if g.Limits[0] == "" && g.Limits[1] == "" {
|
||||||
sw.col.Scan(s.desc, sw,
|
sw.col.Scan(s.desc, sw,
|
||||||
func(id string, o geojson.Object, fields []float64) bool {
|
func(id string, o geojson.Object, fields *collection.Fields) bool {
|
||||||
return sw.writeObject(ScanWriterParams{
|
return sw.writeObject(ScanWriterParams{
|
||||||
id: id,
|
id: id,
|
||||||
o: o,
|
o: o,
|
||||||
|
@ -77,7 +78,7 @@ func (c *Server) cmdScan(msg *Message) (res resp.Value, err error) {
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
sw.col.ScanRange(g.Limits[0], g.Limits[1], s.desc, sw,
|
sw.col.ScanRange(g.Limits[0], g.Limits[1], s.desc, sw,
|
||||||
func(id string, o geojson.Object, fields []float64) bool {
|
func(id string, o geojson.Object, fields *collection.Fields) bool {
|
||||||
return sw.writeObject(ScanWriterParams{
|
return sw.writeObject(ScanWriterParams{
|
||||||
id: id,
|
id: id,
|
||||||
o: o,
|
o: o,
|
||||||
|
|
|
@ -64,7 +64,7 @@ type scanWriter struct {
|
||||||
type ScanWriterParams struct {
|
type ScanWriterParams struct {
|
||||||
id string
|
id string
|
||||||
o geojson.Object
|
o geojson.Object
|
||||||
fields []float64
|
fields *collection.Fields
|
||||||
distance float64
|
distance float64
|
||||||
noLock bool
|
noLock bool
|
||||||
ignoreGlobMatch bool
|
ignoreGlobMatch bool
|
||||||
|
@ -194,7 +194,9 @@ func (sw *scanWriter) writeFoot() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) (fvals []float64, match bool) {
|
func (sw *scanWriter) fieldMatch(
|
||||||
|
fields *collection.Fields, o geojson.Object,
|
||||||
|
) (fvals []float64, match bool) {
|
||||||
var z float64
|
var z float64
|
||||||
var gotz bool
|
var gotz bool
|
||||||
fvals = sw.fvals
|
fvals = sw.fvals
|
||||||
|
@ -212,9 +214,7 @@ func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) (fvals []fl
|
||||||
var value float64
|
var value float64
|
||||||
idx, ok := sw.fmap[where.field]
|
idx, ok := sw.fmap[where.field]
|
||||||
if ok {
|
if ok {
|
||||||
if len(fields) > idx {
|
value = fields.Get(idx)
|
||||||
value = fields[idx]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !where.match(value) {
|
if !where.match(value) {
|
||||||
return
|
return
|
||||||
|
@ -224,9 +224,7 @@ func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) (fvals []fl
|
||||||
var value float64
|
var value float64
|
||||||
idx, ok := sw.fmap[wherein.field]
|
idx, ok := sw.fmap[wherein.field]
|
||||||
if ok {
|
if ok {
|
||||||
if len(fields) > idx {
|
value = fields.Get(idx)
|
||||||
value = fields[idx]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !wherein.match(value) {
|
if !wherein.match(value) {
|
||||||
return
|
return
|
||||||
|
@ -235,11 +233,7 @@ func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) (fvals []fl
|
||||||
for _, whereval := range sw.whereevals {
|
for _, whereval := range sw.whereevals {
|
||||||
fieldsWithNames := make(map[string]float64)
|
fieldsWithNames := make(map[string]float64)
|
||||||
for field, idx := range sw.fmap {
|
for field, idx := range sw.fmap {
|
||||||
if idx < len(fields) {
|
fieldsWithNames[field] = fields.Get(idx)
|
||||||
fieldsWithNames[field] = fields[idx]
|
|
||||||
} else {
|
|
||||||
fieldsWithNames[field] = 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !whereval.match(fieldsWithNames) {
|
if !whereval.match(fieldsWithNames) {
|
||||||
return
|
return
|
||||||
|
@ -247,11 +241,7 @@ func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) (fvals []fl
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for idx := range sw.farr {
|
for idx := range sw.farr {
|
||||||
var value float64
|
sw.fvals[idx] = fields.Get(idx)
|
||||||
if len(fields) > idx {
|
|
||||||
value = fields[idx]
|
|
||||||
}
|
|
||||||
sw.fvals[idx] = value
|
|
||||||
}
|
}
|
||||||
for _, where := range sw.wheres {
|
for _, where := range sw.wheres {
|
||||||
if where.field == "z" {
|
if where.field == "z" {
|
||||||
|
@ -285,11 +275,7 @@ func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) (fvals []fl
|
||||||
for _, whereval := range sw.whereevals {
|
for _, whereval := range sw.whereevals {
|
||||||
fieldsWithNames := make(map[string]float64)
|
fieldsWithNames := make(map[string]float64)
|
||||||
for field, idx := range sw.fmap {
|
for field, idx := range sw.fmap {
|
||||||
if idx < len(fields) {
|
fieldsWithNames[field] = fields.Get(idx)
|
||||||
fieldsWithNames[field] = fields[idx]
|
|
||||||
} else {
|
|
||||||
fieldsWithNames[field] = 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !whereval.match(fieldsWithNames) {
|
if !whereval.match(fieldsWithNames) {
|
||||||
return
|
return
|
||||||
|
@ -333,7 +319,10 @@ func (sw *scanWriter) Step(n uint64) {
|
||||||
|
|
||||||
// ok is whether the object passes the test and should be written
|
// ok is whether the object passes the test and should be written
|
||||||
// keepGoing is whether there could be more objects to test
|
// keepGoing is whether there could be more objects to test
|
||||||
func (sw *scanWriter) testObject(id string, o geojson.Object, fields []float64, ignoreGlobMatch bool) (
|
func (sw *scanWriter) testObject(
|
||||||
|
id string, o geojson.Object,
|
||||||
|
fields *collection.Fields, ignoreGlobMatch bool,
|
||||||
|
) (
|
||||||
ok, keepGoing bool, fieldVals []float64) {
|
ok, keepGoing bool, fieldVals []float64) {
|
||||||
if !ignoreGlobMatch {
|
if !ignoreGlobMatch {
|
||||||
match, kg := sw.globMatch(id, o)
|
match, kg := sw.globMatch(id, o)
|
||||||
|
@ -384,16 +373,15 @@ func (sw *scanWriter) writeObject(opts ScanWriterParams) bool {
|
||||||
jsfields = `,"fields":{`
|
jsfields = `,"fields":{`
|
||||||
var i int
|
var i int
|
||||||
for field, idx := range sw.fmap {
|
for field, idx := range sw.fmap {
|
||||||
if len(opts.fields) > idx {
|
fvalue := opts.fields.Get(idx)
|
||||||
if opts.fields[idx] != 0 {
|
if fvalue != 0 {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
jsfields += `,`
|
jsfields += `,`
|
||||||
}
|
}
|
||||||
jsfields += jsonString(field) + ":" + strconv.FormatFloat(opts.fields[idx], 'f', -1, 64)
|
jsfields += jsonString(field) + ":" + strconv.FormatFloat(fvalue, 'f', -1, 64)
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
jsfields += `}`
|
jsfields += `}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/tidwall/geojson/geometry"
|
"github.com/tidwall/geojson/geometry"
|
||||||
"github.com/tidwall/resp"
|
"github.com/tidwall/resp"
|
||||||
"github.com/tidwall/tile38/internal/bing"
|
"github.com/tidwall/tile38/internal/bing"
|
||||||
|
"github.com/tidwall/tile38/internal/collection"
|
||||||
"github.com/tidwall/tile38/internal/glob"
|
"github.com/tidwall/tile38/internal/glob"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -370,7 +371,12 @@ func (server *Server) cmdNearby(msg *Message) (res resp.Value, err error) {
|
||||||
}
|
}
|
||||||
sw.writeHead()
|
sw.writeHead()
|
||||||
if sw.col != nil {
|
if sw.col != nil {
|
||||||
iter := func(id string, o geojson.Object, fields []float64, dist float64) bool {
|
iter := func(
|
||||||
|
id string,
|
||||||
|
o geojson.Object,
|
||||||
|
fields *collection.Fields,
|
||||||
|
dist float64,
|
||||||
|
) bool {
|
||||||
meters := 0.0
|
meters := 0.0
|
||||||
if s.distance {
|
if s.distance {
|
||||||
meters = geo.DistanceFromHaversine(dist)
|
meters = geo.DistanceFromHaversine(dist)
|
||||||
|
@ -398,18 +404,21 @@ func (server *Server) cmdNearby(msg *Message) (res resp.Value, err error) {
|
||||||
type iterItem struct {
|
type iterItem struct {
|
||||||
id string
|
id string
|
||||||
o geojson.Object
|
o geojson.Object
|
||||||
fields []float64
|
fields *collection.Fields
|
||||||
dist float64
|
dist float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) nearestNeighbors(
|
func (server *Server) nearestNeighbors(
|
||||||
s *liveFenceSwitches, sw *scanWriter, target *geojson.Circle,
|
s *liveFenceSwitches, sw *scanWriter, target *geojson.Circle,
|
||||||
iter func(id string, o geojson.Object, fields []float64, dist float64,
|
iter func(
|
||||||
|
id string, o geojson.Object, fields *collection.Fields, dist float64,
|
||||||
) bool) {
|
) bool) {
|
||||||
maxDist := target.Haversine()
|
maxDist := target.Haversine()
|
||||||
limit := int(sw.limit)
|
limit := int(sw.limit)
|
||||||
var items []iterItem
|
var items []iterItem
|
||||||
sw.col.Nearby(target, sw, func(id string, o geojson.Object, fields []float64) bool {
|
sw.col.Nearby(
|
||||||
|
target, sw,
|
||||||
|
func(id string, o geojson.Object, fields *collection.Fields) bool {
|
||||||
if server.hasExpired(s.key, id) {
|
if server.hasExpired(s.key, id) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -481,7 +490,7 @@ func (server *Server) cmdWithinOrIntersects(cmd string, msg *Message) (res resp.
|
||||||
if sw.col != nil {
|
if sw.col != nil {
|
||||||
if cmd == "within" {
|
if cmd == "within" {
|
||||||
sw.col.Within(s.obj, s.sparse, sw, func(
|
sw.col.Within(s.obj, s.sparse, sw, func(
|
||||||
id string, o geojson.Object, fields []float64,
|
id string, o geojson.Object, fields *collection.Fields,
|
||||||
) bool {
|
) bool {
|
||||||
if server.hasExpired(s.key, id) {
|
if server.hasExpired(s.key, id) {
|
||||||
return true
|
return true
|
||||||
|
@ -497,7 +506,7 @@ func (server *Server) cmdWithinOrIntersects(cmd string, msg *Message) (res resp.
|
||||||
sw.col.Intersects(s.obj, s.sparse, sw, func(
|
sw.col.Intersects(s.obj, s.sparse, sw, func(
|
||||||
id string,
|
id string,
|
||||||
o geojson.Object,
|
o geojson.Object,
|
||||||
fields []float64,
|
fields *collection.Fields,
|
||||||
) bool {
|
) bool {
|
||||||
if server.hasExpired(s.key, id) {
|
if server.hasExpired(s.key, id) {
|
||||||
return true
|
return true
|
||||||
|
@ -579,7 +588,11 @@ func (server *Server) cmdSearch(msg *Message) (res resp.Value, err error) {
|
||||||
g := glob.Parse(sw.globPattern, s.desc)
|
g := glob.Parse(sw.globPattern, s.desc)
|
||||||
if g.Limits[0] == "" && g.Limits[1] == "" {
|
if g.Limits[0] == "" && g.Limits[1] == "" {
|
||||||
sw.col.SearchValues(s.desc, sw,
|
sw.col.SearchValues(s.desc, sw,
|
||||||
func(id string, o geojson.Object, fields []float64) bool {
|
func(
|
||||||
|
id string,
|
||||||
|
o geojson.Object,
|
||||||
|
fields *collection.Fields,
|
||||||
|
) bool {
|
||||||
return sw.writeObject(ScanWriterParams{
|
return sw.writeObject(ScanWriterParams{
|
||||||
id: id,
|
id: id,
|
||||||
o: o,
|
o: o,
|
||||||
|
@ -593,7 +606,11 @@ func (server *Server) cmdSearch(msg *Message) (res resp.Value, err error) {
|
||||||
// globSingle is only for ID matches, not values.
|
// globSingle is only for ID matches, not values.
|
||||||
sw.globSingle = false
|
sw.globSingle = false
|
||||||
sw.col.SearchValuesRange(g.Limits[0], g.Limits[1], s.desc, sw,
|
sw.col.SearchValuesRange(g.Limits[0], g.Limits[1], s.desc, sw,
|
||||||
func(id string, o geojson.Object, fields []float64) bool {
|
func(
|
||||||
|
id string,
|
||||||
|
o geojson.Object,
|
||||||
|
fields *collection.Fields,
|
||||||
|
) bool {
|
||||||
return sw.writeObject(ScanWriterParams{
|
return sw.writeObject(ScanWriterParams{
|
||||||
id: id,
|
id: id,
|
||||||
o: o,
|
o: o,
|
||||||
|
|
|
@ -50,11 +50,11 @@ type commandDetails struct {
|
||||||
command string // client command, like "SET" or "DEL"
|
command string // client command, like "SET" or "DEL"
|
||||||
key, id string // collection key and object id of object
|
key, id string // collection key and object id of object
|
||||||
newKey string // new key, for RENAME command
|
newKey string // new key, for RENAME command
|
||||||
fmap map[string]int // map of field names to value indexes
|
|
||||||
obj geojson.Object // new object
|
obj geojson.Object // new object
|
||||||
fields []float64 // array of field values
|
fmap map[string]int // map of field names to value indexes
|
||||||
|
fields *collection.Fields // array of field values
|
||||||
oldObj geojson.Object // previous object, if any
|
oldObj geojson.Object // previous object, if any
|
||||||
oldFields []float64 // previous object field values
|
oldFields *collection.Fields // previous object field values
|
||||||
updated bool // object was updated
|
updated bool // object was updated
|
||||||
timestamp time.Time // timestamp when the update occured
|
timestamp time.Time // timestamp when the update occured
|
||||||
parent bool // when true, only children are forwarded
|
parent bool // when true, only children are forwarded
|
||||||
|
|
Loading…
Reference in New Issue