tile38/internal/controller/json.go

329 lines
7.3 KiB
Go
Raw Normal View History

2016-03-05 02:08:16 +03:00
package controller
import (
"bytes"
"encoding/json"
"strconv"
"strings"
"time"
"github.com/tidwall/geojson"
"github.com/tidwall/gjson"
"github.com/tidwall/resp"
"github.com/tidwall/sjson"
"github.com/tidwall/tile38/internal/collection"
"github.com/tidwall/tile38/internal/server"
)
2016-03-05 02:08:16 +03:00
2016-12-28 21:16:28 +03:00
func appendJSONString(b []byte, s string) []byte {
for i := 0; i < len(s); i++ {
if s[i] < ' ' || s[i] == '\\' || s[i] == '"' || s[i] > 126 {
d, _ := json.Marshal(s)
return append(b, string(d)...)
}
}
b = append(b, '"')
b = append(b, s...)
b = append(b, '"')
return b
}
2016-03-05 02:08:16 +03:00
func jsonString(s string) string {
for i := 0; i < len(s); i++ {
2016-09-11 17:49:48 +03:00
if s[i] < ' ' || s[i] == '\\' || s[i] == '"' || s[i] > 126 {
2016-03-05 02:08:16 +03:00
d, _ := json.Marshal(s)
return string(d)
}
}
2016-09-11 17:49:48 +03:00
b := make([]byte, len(s)+2)
b[0] = '"'
2016-11-07 23:04:21 +03:00
copy(b[1:], s)
2016-09-11 17:49:48 +03:00
b[len(b)-1] = '"'
2016-11-07 23:04:21 +03:00
return string(b)
2016-03-05 02:08:16 +03:00
}
func appendJSONSimpleBounds(dst []byte, o geojson.Object) []byte {
bbox := o.Rect()
dst = append(dst, `{"sw":{"lat":`...)
dst = strconv.AppendFloat(dst, bbox.Min.Y, 'f', -1, 64)
dst = append(dst, `,"lon":`...)
dst = strconv.AppendFloat(dst, bbox.Min.X, 'f', -1, 64)
dst = append(dst, `},"ne":{"lat":`...)
dst = strconv.AppendFloat(dst, bbox.Max.Y, 'f', -1, 64)
dst = append(dst, `,"lon":`...)
dst = strconv.AppendFloat(dst, bbox.Max.X, 'f', -1, 64)
dst = append(dst, `}}`...)
return dst
}
func appendJSONSimplePoint(dst []byte, o geojson.Object) []byte {
point := o.Center()
var z float64
if gPoint, ok := o.(*geojson.Point); ok {
z = gPoint.Z()
}
dst = append(dst, `{"lat":`...)
dst = strconv.AppendFloat(dst, point.Y, 'f', -1, 64)
dst = append(dst, `,"lon":`...)
dst = strconv.AppendFloat(dst, point.X, 'f', -1, 64)
if z != 0 {
dst = append(dst, `,"z":`...)
dst = strconv.AppendFloat(dst, z, 'f', -1, 64)
}
dst = append(dst, '}')
return dst
}
2018-08-03 03:36:18 +03:00
func appendJSONTimeFormat(b []byte, t time.Time) []byte {
b = append(b, '"')
b = t.AppendFormat(b, "2006-01-02T15:04:05.999999999Z07:00")
b = append(b, '"')
return b
}
func jsonTimeFormat(t time.Time) string {
var b []byte
b = appendJSONTimeFormat(b, t)
return string(b)
}
2017-10-05 18:20:40 +03:00
func (c *Controller) cmdJget(msg *server.Message) (resp.Value, error) {
start := time.Now()
2017-10-05 18:20:40 +03:00
if len(msg.Values) < 3 {
2017-10-05 18:20:40 +03:00
return server.NOMessage, errInvalidNumberOfArguments
}
if len(msg.Values) > 5 {
2017-10-05 18:20:40 +03:00
return server.NOMessage, errInvalidNumberOfArguments
}
key := msg.Values[1].String()
id := msg.Values[2].String()
var doget bool
var path string
var raw bool
if len(msg.Values) > 3 {
doget = true
path = msg.Values[3].String()
if len(msg.Values) == 5 {
if strings.ToLower(msg.Values[4].String()) == "raw" {
raw = true
} else {
2017-10-05 18:20:40 +03:00
return server.NOMessage, errInvalidArgument(msg.Values[4].String())
}
}
}
col := c.getCol(key)
if col == nil {
if msg.OutputType == server.RESP {
2017-10-05 18:20:40 +03:00
return resp.NullValue(), nil
}
2017-10-05 18:20:40 +03:00
return server.NOMessage, errKeyNotFound
}
o, _, ok := col.Get(id)
if !ok {
if msg.OutputType == server.RESP {
2017-10-05 18:20:40 +03:00
return resp.NullValue(), nil
}
2017-10-05 18:20:40 +03:00
return server.NOMessage, errIDNotFound
}
var res gjson.Result
if doget {
res = gjson.Get(o.String(), path)
} else {
res = gjson.Parse(o.String())
}
var val string
if raw {
val = res.Raw
} else {
val = res.String()
}
var buf bytes.Buffer
if msg.OutputType == server.JSON {
buf.WriteString(`{"ok":true`)
}
switch msg.OutputType {
case server.JSON:
if res.Exists() {
buf.WriteString(`,"value":` + jsonString(val))
}
buf.WriteString(`,"elapsed":"` + time.Now().Sub(start).String() + "\"}")
2017-10-05 18:20:40 +03:00
return resp.StringValue(buf.String()), nil
case server.RESP:
if !res.Exists() {
2017-10-05 18:20:40 +03:00
return resp.NullValue(), nil
}
2017-10-05 18:20:40 +03:00
return resp.StringValue(val), nil
}
2017-10-05 18:20:40 +03:00
return server.NOMessage, nil
}
2017-10-05 18:20:40 +03:00
func (c *Controller) cmdJset(msg *server.Message) (res resp.Value, d commandDetailsT, err error) {
// JSET key path value [RAW]
start := time.Now()
2017-10-05 18:20:40 +03:00
var raw, str bool
switch len(msg.Values) {
default:
2017-10-05 18:20:40 +03:00
return server.NOMessage, d, errInvalidNumberOfArguments
case 5:
case 6:
switch strings.ToLower(msg.Values[5].String()) {
default:
2017-10-05 18:20:40 +03:00
return server.NOMessage, d, errInvalidArgument(msg.Values[5].String())
case "raw":
raw = true
case "str":
str = true
}
}
key := msg.Values[1].String()
id := msg.Values[2].String()
path := msg.Values[3].String()
val := msg.Values[4].String()
if !str && !raw {
switch val {
default:
if len(val) > 0 {
if (val[0] >= '0' && val[0] <= '9') || val[0] == '-' {
if _, err := strconv.ParseFloat(val, 64); err == nil {
raw = true
}
}
}
case "true", "false", "null":
raw = true
}
}
col := c.getCol(key)
var createcol bool
if col == nil {
col = collection.New()
createcol = true
}
var json string
var geoobj bool
o, _, ok := col.Get(id)
if ok {
geoobj = objIsSpatial(o)
json = o.String()
}
if raw {
// set as raw block
json, err = sjson.SetRaw(json, path, val)
} else {
// set as a string
json, err = sjson.Set(json, path, val)
}
if err != nil {
2017-10-05 18:20:40 +03:00
return server.NOMessage, d, err
}
if geoobj {
nmsg := *msg
nmsg.Values = []resp.Value{
resp.StringValue("SET"),
resp.StringValue(key),
resp.StringValue(id),
resp.StringValue("OBJECT"),
resp.StringValue(json),
}
// SET key id OBJECT json
return c.cmdSet(&nmsg)
}
if createcol {
c.setCol(key, col)
}
2016-12-14 17:37:02 +03:00
d.key = key
d.id = id
d.obj = collection.String(json)
2016-12-14 17:37:02 +03:00
d.timestamp = time.Now()
d.updated = true
c.clearIDExpires(key, id)
2018-08-16 23:07:55 +03:00
col.Set(d.id, d.obj, nil, nil)
switch msg.OutputType {
case server.JSON:
var buf bytes.Buffer
buf.WriteString(`{"ok":true`)
buf.WriteString(`,"elapsed":"` + time.Now().Sub(start).String() + "\"}")
2017-10-05 18:20:40 +03:00
return resp.StringValue(buf.String()), d, nil
case server.RESP:
2017-10-05 18:20:40 +03:00
return resp.SimpleStringValue("OK"), d, nil
}
2017-10-05 18:20:40 +03:00
return server.NOMessage, d, nil
}
2017-10-05 18:20:40 +03:00
func (c *Controller) cmdJdel(msg *server.Message) (res resp.Value, d commandDetailsT, err error) {
start := time.Now()
2017-10-05 18:20:40 +03:00
if len(msg.Values) != 4 {
2017-10-05 18:20:40 +03:00
return server.NOMessage, d, errInvalidNumberOfArguments
}
key := msg.Values[1].String()
id := msg.Values[2].String()
path := msg.Values[3].String()
col := c.getCol(key)
if col == nil {
if msg.OutputType == server.RESP {
2017-10-05 18:20:40 +03:00
return resp.IntegerValue(0), d, nil
}
2017-10-05 18:20:40 +03:00
return server.NOMessage, d, errKeyNotFound
}
var json string
var geoobj bool
o, _, ok := col.Get(id)
if ok {
geoobj = objIsSpatial(o)
json = o.String()
}
njson, err := sjson.Delete(json, path)
if err != nil {
2017-10-05 18:20:40 +03:00
return server.NOMessage, d, err
}
if njson == json {
switch msg.OutputType {
case server.JSON:
2017-10-05 18:20:40 +03:00
return server.NOMessage, d, errPathNotFound
case server.RESP:
2017-10-05 18:20:40 +03:00
return resp.IntegerValue(0), d, nil
}
2017-10-05 18:20:40 +03:00
return server.NOMessage, d, nil
}
json = njson
if geoobj {
nmsg := *msg
nmsg.Values = []resp.Value{
resp.StringValue("SET"),
resp.StringValue(key),
resp.StringValue(id),
resp.StringValue("OBJECT"),
resp.StringValue(json),
}
// SET key id OBJECT json
return c.cmdSet(&nmsg)
}
2016-12-14 17:37:02 +03:00
d.key = key
d.id = id
d.obj = collection.String(json)
2016-12-14 17:37:02 +03:00
d.timestamp = time.Now()
d.updated = true
c.clearIDExpires(d.key, d.id)
2018-08-16 23:07:55 +03:00
col.Set(d.id, d.obj, nil, nil)
switch msg.OutputType {
case server.JSON:
var buf bytes.Buffer
buf.WriteString(`{"ok":true`)
buf.WriteString(`,"elapsed":"` + time.Now().Sub(start).String() + "\"}")
2017-10-05 18:20:40 +03:00
return resp.StringValue(buf.String()), d, nil
case server.RESP:
2017-10-05 18:20:40 +03:00
return resp.IntegerValue(1), d, nil
}
2017-10-05 18:20:40 +03:00
return server.NOMessage, d, nil
}