mirror of https://github.com/tidwall/tile38.git
Merge branch 'rshura-optimize-field-match'
This commit is contained in:
commit
0d83b1ca53
|
@ -98,8 +98,6 @@ func (s *Server) newScanWriter(
|
||||||
msg: msg,
|
msg: msg,
|
||||||
cursor: cursor,
|
cursor: cursor,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
wheres: wheres,
|
|
||||||
whereins: whereins,
|
|
||||||
whereevals: whereevals,
|
whereevals: whereevals,
|
||||||
output: output,
|
output: output,
|
||||||
nofields: nofields,
|
nofields: nofields,
|
||||||
|
@ -118,6 +116,27 @@ func (s *Server) newScanWriter(
|
||||||
if sw.col != nil {
|
if sw.col != nil {
|
||||||
sw.fmap = sw.col.FieldMap()
|
sw.fmap = sw.col.FieldMap()
|
||||||
sw.farr = sw.col.FieldArr()
|
sw.farr = sw.col.FieldArr()
|
||||||
|
// This fills index value in wheres/whereins
|
||||||
|
// so we don't have to map string field names for each tested object
|
||||||
|
var ok bool
|
||||||
|
if len(wheres) > 0 {
|
||||||
|
sw.wheres = make([]whereT, len(wheres))
|
||||||
|
for i, where := range wheres {
|
||||||
|
if where.index, ok = sw.fmap[where.field]; !ok {
|
||||||
|
where.index = math.MaxInt32
|
||||||
|
}
|
||||||
|
sw.wheres[i] = where
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(whereins) > 0 {
|
||||||
|
sw.whereins = make([]whereinT, len(whereins))
|
||||||
|
for i, wherein := range whereins {
|
||||||
|
if wherein.index, ok = sw.fmap[wherein.field]; !ok {
|
||||||
|
wherein.index = math.MaxInt32
|
||||||
|
}
|
||||||
|
sw.whereins[i] = wherein
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sw.fvals = make([]float64, len(sw.farr))
|
sw.fvals = make([]float64, len(sw.farr))
|
||||||
return sw, nil
|
return sw, nil
|
||||||
|
@ -213,11 +232,8 @@ func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) (fvals []fl
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var value float64
|
var value float64
|
||||||
idx, ok := sw.fmap[where.field]
|
if where.index < len(fields) {
|
||||||
if ok {
|
value = fields[where.index]
|
||||||
if len(fields) > idx {
|
|
||||||
value = fields[idx]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !where.match(value) {
|
if !where.match(value) {
|
||||||
return
|
return
|
||||||
|
@ -225,11 +241,8 @@ func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) (fvals []fl
|
||||||
}
|
}
|
||||||
for _, wherein := range sw.whereins {
|
for _, wherein := range sw.whereins {
|
||||||
var value float64
|
var value float64
|
||||||
idx, ok := sw.fmap[wherein.field]
|
if wherein.index < len(fields) {
|
||||||
if ok {
|
value = fields[wherein.index]
|
||||||
if len(fields) > idx {
|
|
||||||
value = fields[idx]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !wherein.match(value) {
|
if !wherein.match(value) {
|
||||||
return
|
return
|
||||||
|
@ -249,12 +262,10 @@ func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) (fvals []fl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for idx := range sw.farr {
|
copy(sw.fvals, fields)
|
||||||
var value float64
|
// fields might be shorter for this item, need to pad sw.fvals with zeros
|
||||||
if len(fields) > idx {
|
for i := len(fields); i < len(sw.fvals); i++ {
|
||||||
value = fields[idx]
|
sw.fvals[i] = 0
|
||||||
}
|
|
||||||
sw.fvals[idx] = value
|
|
||||||
}
|
}
|
||||||
for _, where := range sw.wheres {
|
for _, where := range sw.wheres {
|
||||||
if where.field == "z" {
|
if where.field == "z" {
|
||||||
|
@ -269,9 +280,8 @@ func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) (fvals []fl
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var value float64
|
var value float64
|
||||||
idx, ok := sw.fmap[where.field]
|
if where.index < len(sw.fvals) {
|
||||||
if ok {
|
value = sw.fvals[where.index]
|
||||||
value = sw.fvals[idx]
|
|
||||||
}
|
}
|
||||||
if !where.match(value) {
|
if !where.match(value) {
|
||||||
return
|
return
|
||||||
|
@ -279,9 +289,8 @@ func (sw *scanWriter) fieldMatch(fields []float64, o geojson.Object) (fvals []fl
|
||||||
}
|
}
|
||||||
for _, wherein := range sw.whereins {
|
for _, wherein := range sw.whereins {
|
||||||
var value float64
|
var value float64
|
||||||
idx, ok := sw.fmap[wherein.field]
|
if wherein.index < len(sw.fvals) {
|
||||||
if ok {
|
value = sw.fvals[wherein.index]
|
||||||
value = sw.fvals[idx]
|
|
||||||
}
|
}
|
||||||
if !wherein.match(value) {
|
if !wherein.match(value) {
|
||||||
return
|
return
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tidwall/geojson"
|
||||||
|
"github.com/tidwall/geojson/geometry"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testPointItem struct {
|
||||||
|
object geojson.Object
|
||||||
|
fields []float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func PO(x, y float64) *geojson.Point {
|
||||||
|
return geojson.NewPoint(geometry.Point{X: x, Y: y})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func BenchmarkFieldMatch(t *testing.B) {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
items := make([]testPointItem, t.N)
|
||||||
|
for i := 0; i < t.N; i++ {
|
||||||
|
items[i] = testPointItem{
|
||||||
|
PO(rand.Float64()*360-180, rand.Float64()*180-90),
|
||||||
|
[]float64{rand.Float64()*9+1, math.Round(rand.Float64()*30) + 1},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sw := &scanWriter{
|
||||||
|
wheres: []whereT{
|
||||||
|
{"foo", 0, false, 1, false, 3},
|
||||||
|
{"bar", 1, false, 10, false, 30},
|
||||||
|
},
|
||||||
|
whereins: []whereinT{
|
||||||
|
{"foo", 0, []float64{1, 2}},
|
||||||
|
{"bar", 1, []float64{11, 25}},
|
||||||
|
},
|
||||||
|
fmap: map[string]int{"foo": 0, "bar": 1},
|
||||||
|
farr: []string{"bar", "foo"},
|
||||||
|
}
|
||||||
|
sw.fvals = make([]float64, len(sw.farr))
|
||||||
|
t.ResetTimer()
|
||||||
|
for i := 0; i < t.N; i++ {
|
||||||
|
// one call is super fast, measurements are not reliable, let's do 100
|
||||||
|
for ix := 0; ix < 100; ix++ {
|
||||||
|
sw.fieldMatch(items[i].fields, items[i].object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -88,6 +88,7 @@ func lc(s1, s2 string) bool {
|
||||||
|
|
||||||
type whereT struct {
|
type whereT struct {
|
||||||
field string
|
field string
|
||||||
|
index int
|
||||||
minx bool
|
minx bool
|
||||||
min float64
|
min float64
|
||||||
maxx bool
|
maxx bool
|
||||||
|
@ -118,12 +119,17 @@ func (where whereT) match(value float64) bool {
|
||||||
|
|
||||||
type whereinT struct {
|
type whereinT struct {
|
||||||
field string
|
field string
|
||||||
valMap map[float64]struct{}
|
index int
|
||||||
|
valArr []float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wherein whereinT) match(value float64) bool {
|
func (wherein whereinT) match(value float64) bool {
|
||||||
_, ok := wherein.valMap[value]
|
for _, val := range wherein.valArr {
|
||||||
return ok
|
if val == value {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
type whereevalT struct {
|
type whereevalT struct {
|
||||||
|
@ -282,7 +288,7 @@ func (s *Server) parseSearchScanBaseTokens(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.wheres = append(t.wheres, whereT{field, minx, min, maxx, max})
|
t.wheres = append(t.wheres, whereT{field, -1, minx, min, maxx, max})
|
||||||
continue
|
continue
|
||||||
case "wherein":
|
case "wherein":
|
||||||
vs = nvs
|
vs = nvs
|
||||||
|
@ -300,9 +306,8 @@ func (s *Server) parseSearchScanBaseTokens(
|
||||||
err = errInvalidArgument(nvalsStr)
|
err = errInvalidArgument(nvalsStr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
valMap := make(map[float64]struct{})
|
valArr := make([]float64, nvals)
|
||||||
var val float64
|
var val float64
|
||||||
var empty struct{}
|
|
||||||
for i = 0; i < nvals; i++ {
|
for i = 0; i < nvals; i++ {
|
||||||
if vs, valStr, ok = tokenval(vs); !ok || valStr == "" {
|
if vs, valStr, ok = tokenval(vs); !ok || valStr == "" {
|
||||||
err = errInvalidNumberOfArguments
|
err = errInvalidNumberOfArguments
|
||||||
|
@ -312,9 +317,9 @@ func (s *Server) parseSearchScanBaseTokens(
|
||||||
err = errInvalidArgument(valStr)
|
err = errInvalidArgument(valStr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
valMap[val] = empty
|
valArr[i] = val
|
||||||
}
|
}
|
||||||
t.whereins = append(t.whereins, whereinT{field, valMap})
|
t.whereins = append(t.whereins, whereinT{field, -1, valArr})
|
||||||
continue
|
continue
|
||||||
case "whereevalsha":
|
case "whereevalsha":
|
||||||
fallthrough
|
fallthrough
|
||||||
|
|
|
@ -394,6 +394,9 @@ func keys_WHEREIN_test(mc *mockServer) error {
|
||||||
{"SET", "mykey", "myid_a2", "FIELD", "a", 2, "POINT", 32.99, -115}, {"OK"},
|
{"SET", "mykey", "myid_a2", "FIELD", "a", 2, "POINT", 32.99, -115}, {"OK"},
|
||||||
{"SET", "mykey", "myid_a3", "FIELD", "a", 3, "POINT", 33, -115.02}, {"OK"},
|
{"SET", "mykey", "myid_a3", "FIELD", "a", 3, "POINT", 33, -115.02}, {"OK"},
|
||||||
{"WITHIN", "mykey", "WHEREIN", "a", 3, 0, 1, 2, "BOUNDS", 32.8, -115.2, 33.2, -114.8}, {`[0 [[myid_a1 {"type":"Point","coordinates":[-115,33]} [a 1]] [myid_a2 {"type":"Point","coordinates":[-115,32.99]} [a 2]]]]`},
|
{"WITHIN", "mykey", "WHEREIN", "a", 3, 0, 1, 2, "BOUNDS", 32.8, -115.2, 33.2, -114.8}, {`[0 [[myid_a1 {"type":"Point","coordinates":[-115,33]} [a 1]] [myid_a2 {"type":"Point","coordinates":[-115,32.99]} [a 2]]]]`},
|
||||||
|
// zero value should not match 1 and 2
|
||||||
|
{"SET", "mykey", "myid_a0", "FIELD", "a", 0, "POINT", 33, -115.02}, {"OK"},
|
||||||
|
{"WITHIN", "mykey", "WHEREIN", "a", 2, 1, 2, "BOUNDS", 32.8, -115.2, 33.2, -114.8}, {`[0 [[myid_a1 {"type":"Point","coordinates":[-115,33]} [a 1]] [myid_a2 {"type":"Point","coordinates":[-115,32.99]} [a 2]]]]`},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue