
It's now possible to query a JSON field using a GJSON path. SET fleet truck1 FIELD props '{"speed":58,"name":"Andy"}' POINT 33 -112 You can then use the GJSON type path to return the objects that match the WHERE. SCAN fleet WHERE props.speed 50 inf SCAN fleet WHERE props.name Andy Andy Included in this commit is support for '==', '<', '>', '<=', '>=', and '!='. The previous queries could be written like: SCAN fleet WHERE props.speed > 50 SCAN fleet WHERE props.name == Andy
233 lines
5.6 KiB
Go
233 lines
5.6 KiB
Go
package field
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/tidwall/assert"
|
|
"github.com/tidwall/btree"
|
|
)
|
|
|
|
func TestList(t *testing.T) {
|
|
var fields List
|
|
|
|
fields = fields.Set(Make("hello", "123"))
|
|
assert.Assert(fields.Len() == 1)
|
|
// println(fields.Weight())
|
|
// assert.Assert(fields.Weight() == 16)
|
|
|
|
fields = fields.Set(Make("jello", "456"))
|
|
assert.Assert(fields.Len() == 2)
|
|
// assert.Assert(fields.Weight() == 32)
|
|
|
|
value := fields.Get("jello")
|
|
assert.Assert(value.Value().Data() == "456")
|
|
assert.Assert(value.Value().JSON() == "456")
|
|
assert.Assert(value.Value().Num() == 456)
|
|
|
|
value = fields.Get("nello")
|
|
assert.Assert(value.Name() == "")
|
|
assert.Assert(value.Value().IsZero())
|
|
|
|
fields = fields.Set(Make("jello", "789"))
|
|
assert.Assert(fields.Len() == 2)
|
|
// assert.Assert(fields.Weight() == 32)
|
|
|
|
fields = fields.Set(Make("nello", "0"))
|
|
assert.Assert(fields.Len() == 2)
|
|
// assert.Assert(fields.Weight() == 32)
|
|
|
|
fields = fields.Set(Make("jello", "789"))
|
|
assert.Assert(fields.Len() == 2)
|
|
// assert.Assert(fields.Weight() == 32)
|
|
|
|
fields = fields.Set(Make("jello", "0"))
|
|
assert.Assert(fields.Len() == 1)
|
|
// assert.Assert(fields.Weight() == 16)
|
|
|
|
fields = fields.Set(Make("nello", "012"))
|
|
fields = fields.Set(Make("hello", "456"))
|
|
fields = fields.Set(Make("fello", "123"))
|
|
fields = fields.Set(Make("jello", "789"))
|
|
|
|
var names string
|
|
var datas string
|
|
var nums float64
|
|
fields.Scan(func(f Field) bool {
|
|
names += f.Name()
|
|
datas += f.Value().Data()
|
|
nums += f.Value().Num()
|
|
return true
|
|
})
|
|
assert.Assert(names == "fellohellojellonello")
|
|
assert.Assert(datas == "123456789012")
|
|
assert.Assert(nums == 1380)
|
|
|
|
names = ""
|
|
datas = ""
|
|
nums = 0
|
|
fields.Scan(func(f Field) bool {
|
|
names += f.Name()
|
|
datas += f.Value().Data()
|
|
nums += f.Value().Num()
|
|
return false
|
|
})
|
|
assert.Assert(names == "fello")
|
|
assert.Assert(datas == "123")
|
|
assert.Assert(nums == 123)
|
|
|
|
}
|
|
|
|
func randStr(n int) string {
|
|
b := make([]byte, n)
|
|
rand.Read(b)
|
|
for i := 0; i < n; i++ {
|
|
b[i] = 'a' + b[i]%26
|
|
}
|
|
return string(b)
|
|
}
|
|
|
|
func randVal(n int) string {
|
|
switch rand.Intn(10) {
|
|
case 0:
|
|
return "null"
|
|
case 1:
|
|
return "true"
|
|
case 2:
|
|
return "false"
|
|
case 3:
|
|
return `{"a":"` + randStr(n) + `"}`
|
|
case 4:
|
|
return `["` + randStr(n) + `"]`
|
|
case 5:
|
|
return `"` + randStr(n) + `"`
|
|
case 6:
|
|
return randStr(n)
|
|
default:
|
|
return fmt.Sprintf("%f", rand.Float64()*360)
|
|
}
|
|
}
|
|
|
|
func TestRandom(t *testing.T) {
|
|
seed := time.Now().UnixNano()
|
|
// seed = 1663607868546669000
|
|
rand.Seed(seed)
|
|
start := time.Now()
|
|
var total int
|
|
for time.Since(start) < time.Second*2 {
|
|
N := rand.Intn(500)
|
|
var org []Field
|
|
var tr btree.Map[string, Field]
|
|
var fields List
|
|
for i := 0; i < N; i++ {
|
|
name := randStr(rand.Intn(10))
|
|
value := randVal(rand.Intn(10))
|
|
field := Make(name, value)
|
|
org = append(org, field)
|
|
fields = fields.Set(field)
|
|
v := fields.Get(name)
|
|
// println(name, v.Value().Data(), field.Value().Data())
|
|
if !v.Value().Equals(field.Value()) {
|
|
t.Fatalf("seed: %d, expected true", seed)
|
|
}
|
|
tr.Set(name, field)
|
|
if fields.Len() != tr.Len() {
|
|
t.Fatalf("seed: %d, expected %d, got %d",
|
|
seed, tr.Len(), fields.Len())
|
|
}
|
|
}
|
|
comp := func() {
|
|
var all []Field
|
|
fields.Scan(func(f Field) bool {
|
|
all = append(all, f)
|
|
return true
|
|
})
|
|
if len(all) != fields.Len() {
|
|
t.Fatalf("seed: %d, expected %d, got %d",
|
|
seed, fields.Len(), len(all))
|
|
}
|
|
if fields.Len() != tr.Len() {
|
|
t.Fatalf("seed: %d, expected %d, got %d",
|
|
seed, tr.Len(), fields.Len())
|
|
}
|
|
var i int
|
|
tr.Scan(func(name string, f Field) bool {
|
|
if name != f.Name() || all[i].Name() != f.Name() {
|
|
t.Fatalf("seed: %d, out of order", seed)
|
|
}
|
|
i++
|
|
return true
|
|
})
|
|
}
|
|
comp()
|
|
rand.Shuffle(len(org), func(i, j int) {
|
|
org[i], org[j] = org[j], org[i]
|
|
})
|
|
for _, f := range org {
|
|
comp()
|
|
tr.Delete(f.Name())
|
|
fields = fields.Set(Make(f.Name(), "0"))
|
|
if fields.Len() != tr.Len() {
|
|
t.Fatalf("seed: %d, expected %d, got %d",
|
|
seed, tr.Len(), fields.Len())
|
|
}
|
|
comp()
|
|
}
|
|
total++
|
|
}
|
|
|
|
}
|
|
|
|
func TestJSONGet(t *testing.T) {
|
|
|
|
var list List
|
|
list = list.Set(Make("hello", "world"))
|
|
list = list.Set(Make("hello", `"world"`))
|
|
list = list.Set(Make("jello", "planet"))
|
|
list = list.Set(Make("telly", `{"a":[1,2,3],"b":null,"c":true,"d":false}`))
|
|
list = list.Set(Make("belly", `{"a":{"b":{"c":"fancy"}}}`))
|
|
json := list.String()
|
|
exp := `{"belly":{"a":{"b":{"c":"fancy"}}},"hello":"world","jello":` +
|
|
`"planet","telly":{"a":[1,2,3],"b":null,"c":true,"d":false}}`
|
|
if json != exp {
|
|
t.Fatalf("expected '%s', got '%s'", exp, json)
|
|
}
|
|
data := list.Get("hello").Value().Data()
|
|
if data != "world" {
|
|
t.Fatalf("expected '%s', got '%s'", "world", data)
|
|
}
|
|
data = list.Get("telly").Value().Data()
|
|
if data != `{"a":[1,2,3],"b":null,"c":true,"d":false}` {
|
|
t.Fatalf("expected '%s', got '%s'",
|
|
`{"a":[1,2,3],"b":null,"c":true,"d":false}`, data)
|
|
}
|
|
data = list.Get("belly").Value().Data()
|
|
if data != `{"a":{"b":{"c":"fancy"}}}` {
|
|
t.Fatalf("expected '%s', got '%s'",
|
|
`{"a":{"b":{"c":"fancy"}}}`, data)
|
|
}
|
|
data = list.Get("belly.a").Value().Data()
|
|
if data != `{"b":{"c":"fancy"}}` {
|
|
t.Fatalf("expected '%s', got '%s'",
|
|
`{"b":{"c":"fancy"}}`, data)
|
|
}
|
|
data = list.Get("belly.a.b").Value().Data()
|
|
if data != `{"c":"fancy"}` {
|
|
t.Fatalf("expected '%s', got '%s'",
|
|
`{"c":"fancy"}`, data)
|
|
}
|
|
data = list.Get("belly.a.b.c").Value().Data()
|
|
if data != `fancy` {
|
|
t.Fatalf("expected '%s', got '%s'",
|
|
`fancy`, data)
|
|
}
|
|
// Tile38 defaults non-existent fields to zero.
|
|
data = list.Get("belly.a.b.c.d").Value().Data()
|
|
if data != `0` {
|
|
t.Fatalf("expected '%s', got '%s'",
|
|
`0`, data)
|
|
}
|
|
}
|