diff --git a/internal/server/crud.go b/internal/server/crud.go index ab0bbdee..4bf1a967 100644 --- a/internal/server/crud.go +++ b/internal/server/crud.go @@ -168,6 +168,12 @@ func (s *Server) cmdGET(msg *Message) (resp.Value, error) { // >> Response + oval := buildObjectResponse(msg, o, start, kind, precision, withfields, msg.OutputType == JSON) + + return oval, nil +} + +func buildObjectResponse(msg *Message, o *object.Object, start time.Time, kind string, precision int64, withfields, json bool) resp.Value { vals := make([]resp.Value, 0, 2) var buf bytes.Buffer if msg.OutputType == JSON { @@ -240,7 +246,7 @@ func (s *Server) cmdGET(msg *Message) (resp.Value, error) { } var i int o.Fields().Scan(func(f field.Field) bool { - if msg.OutputType == JSON { + if json { if i > 0 { buf.WriteString(`,`) } @@ -253,16 +259,16 @@ func (s *Server) cmdGET(msg *Message) (resp.Value, error) { i++ return true }) - if msg.OutputType == JSON { + if json { buf.WriteString(`}`) } else { vals = append(vals, resp.ArrayValue(fvals)) } } } - if msg.OutputType == JSON { + if json { buf.WriteString(`,"elapsed":"` + time.Since(start).String() + "\"}") - return resp.StringValue(buf.String()), nil + return resp.StringValue(buf.String()) } var oval resp.Value if withfields { @@ -270,7 +276,7 @@ func (s *Server) cmdGET(msg *Message) (resp.Value, error) { } else { oval = vals[0] } - return oval, nil + return oval } // DEL key id [ERRON404] @@ -622,6 +628,10 @@ func (s *Server) cmdSET(msg *Message) (resp.Value, commandDetails, error) { var ex int64 var xx bool var nx bool + var ret bool + var withfields bool + kind := "object" + var precision int64 var oobj geojson.Object args := msg.Args @@ -665,6 +675,43 @@ func (s *Server) cmdSET(msg *Message) (resp.Value, commandDetails, error) { return retwerr(errInvalidArgument(args[i])) } xx = true + case "return": + if ret { + return retwerr(errInvalidArgument(args[i])) + } + ret = true + + for j := i; j < i+3; j++ { + if j >= len(args) { + break + } + switch strings.ToLower(args[j]) { + case "withfields": + withfields = true + i += 1 + case "object": + kind = "object" + i += 1 + case "point": + kind = "point" + i += 1 + case "bounds": + kind = "bounds" + i += 1 + case "hash": + kind = "hash" + j++ + if j == len(args) { + return retwerr(errInvalidNumberOfArguments) + } + var err error + precision, err = strconv.ParseInt(args[j], 10, 64) + if err != nil || precision < 1 || precision > 12 { + return retwerr(errInvalidArgument(args[j])) + } + i += 2 + } + } case "string": if i+1 >= len(args) { return retwerr(errInvalidNumberOfArguments) @@ -802,12 +849,16 @@ func (s *Server) cmdSET(msg *Message) (resp.Value, commandDetails, error) { d.updated = true // perhaps we should do a diff on the previous object? d.timestamp = time.Now() + if ret { + res := buildObjectResponse(msg, obj, start, kind, precision, withfields, msg.OutputType == JSON) + return res, d, nil + } + var res resp.Value switch msg.OutputType { default: case JSON: - res = resp.StringValue(`{"ok":true,"elapsed":"` + - time.Since(start).String() + "\"}") + res = resp.StringValue(`{"ok":true,"elapsed":"` + time.Since(start).String() + "\"}") case RESP: res = resp.SimpleStringValue("OK") } @@ -833,6 +884,11 @@ func (s *Server) cmdFSET(msg *Message) (resp.Value, commandDetails, error) { var id string var key string var xx bool + var ret bool + var withfields bool + kind := "object" + var precision int64 + var fields []field.Field // raw fields args := msg.Args @@ -845,6 +901,44 @@ func (s *Server) cmdFSET(msg *Message) (resp.Value, commandDetails, error) { switch strings.ToLower(arg) { case "xx": xx = true + case "return": + if ret { + return retwerr(errInvalidArgument(args[i])) + } + ret = true + + for j := i; j < i+3; j++ { + if j >= len(args) { + break + } + switch strings.ToLower(args[j]) { + case "withfields": + withfields = true + i += 1 + case "object": + kind = "object" + i += 1 + case "point": + kind = "point" + i += 1 + case "bounds": + kind = "bounds" + i += 1 + case "hash": + kind = "hash" + j++ + if j == len(args) { + return retwerr(errInvalidNumberOfArguments) + } + var err error + precision, err = strconv.ParseInt(args[j], 10, 64) + if err != nil || precision < 1 || precision > 12 { + return retwerr(errInvalidArgument(args[j])) + } + i += 2 + } + } + default: fkey := arg i++ @@ -896,6 +990,11 @@ func (s *Server) cmdFSET(msg *Message) (resp.Value, commandDetails, error) { var res resp.Value + if ret { + res := buildObjectResponse(msg, d.obj, start, kind, precision, withfields, msg.OutputType == JSON) + return res, d, nil + } + switch msg.OutputType { case JSON: res = resp.StringValue(`{"ok":true,"elapsed":"` + diff --git a/tests/keys_test.go b/tests/keys_test.go index 1b14329d..e0855e47 100644 --- a/tests/keys_test.go +++ b/tests/keys_test.go @@ -180,6 +180,12 @@ func keys_FSET_test(mc *mockServer) error { Do("FSET", "mykey", "myid", "f2", 0).Str("1"), Do("GET", "mykey", "myid", "WITHFIELDS", "HASH", 7).Str("[9my5xp7]"), Do("FSET", "mykey", "myid2", "xx", "f1", 1.1, "f2", 2.2).Str("0"), + + Do("FSET", "mykey", "myid", "f1", 1, "RETURN", "HASH", 7, "WITHFIELDS").Str("[9my5xp7 [f1 1]]"), + Do("FSET", "mykey", "myid", "f2", 2, "RETURN", "HASH", 7, "WITHFIELDS").Str("[9my5xp7 [f1 1 f2 2]]"), + Do("FSET", "mykey", "myid", "f1", 0, "RETURN", "HASH", 7, "WITHFIELDS").Str("[9my5xp7 [f2 2]]"), + Do("FSET", "mykey", "myid", "f2", 0, "RETURN", "HASH", 7, "WITHFIELDS").Str("[9my5xp7]"), + Do("GET", "mykey", "myid2").Str(""), Do("DEL", "mykey", "myid").Str("1"), Do("GET", "mykey", "myid").Str(""), @@ -333,6 +339,15 @@ func keys_SET_test(mc *mockServer) error { Do("GET", "mykey", "myid").Str(""), Do("SET", "mykey", "myid", "point", "33", "-112", "99").OK(), + Do("SET", "mykey", "myid", "POINT", 33, -115, "RETURN").Str(`{"type":"Point","coordinates":[-115,33]}`), + Do("SET", "mykey", "myid", "POINT", 33, -115, "RETURN", "POINT").Str("[33 -115]"), + Do("SET", "mykey", "myid", "POINT", 33, -115, "RETURN", "OBJECT").Str(`{"type":"Point","coordinates":[-115,33]}`), + Do("SET", "mykey", "myid", "POINT", 33, -115, "RETURN", "BOUNDS").Str("[[33 -115] [33 -115]]"), + Do("SET", "mykey", "myid", "POINT", 33, -115, "RETURN", "OBJECT").Str(`{"type":"Point","coordinates":[-115,33]}`), + Do("SET", "mykey", "myid", "POINT", 33, -115, "RETURN", "HASH", 7).Str("9my5xp7"), + Do("DEL", "mykey", "myid").Str("1"), + Do("GET", "mykey", "myid").Str(""), + // Section: object Do("SET", "mykey", "myid", "OBJECT", `{"type":"Point","coordinates":[-115,33]}`).OK(), Do("GET", "mykey", "myid", "POINT").Str("[33 -115]"), @@ -342,6 +357,14 @@ func keys_SET_test(mc *mockServer) error { Do("DEL", "mykey", "myid").Str("1"), Do("GET", "mykey", "myid").Str(""), + Do("SET", "mykey", "myid", "OBJECT", `{"type":"Point","coordinates":[-115,33]}`, "RETURN").Str(`{"type":"Point","coordinates":[-115,33]}`), + Do("SET", "mykey", "myid", "OBJECT", `{"type":"Point","coordinates":[-115,33]}`, "RETURN", "POINT").Str("[33 -115]"), + Do("SET", "mykey", "myid", "OBJECT", `{"type":"Point","coordinates":[-115,33]}`, "RETURN", "BOUNDS").Str("[[33 -115] [33 -115]]"), + Do("SET", "mykey", "myid", "OBJECT", `{"type":"Point","coordinates":[-115,33]}`, "RETURN", "OBJECT").Str(`{"type":"Point","coordinates":[-115,33]}`), + Do("SET", "mykey", "myid", "OBJECT", `{"type":"Point","coordinates":[-115,33]}`, "RETURN", "HASH", 7).Str("9my5xp7"), + Do("DEL", "mykey", "myid").Str("1"), + Do("GET", "mykey", "myid").Str(""), + // Section: bounds Do("SET", "mykey", "myid", "BOUNDS", 33, -115, 33, -115).OK(), Do("GET", "mykey", "myid", "POINT").Str("[33 -115]"), @@ -351,6 +374,14 @@ func keys_SET_test(mc *mockServer) error { Do("DEL", "mykey", "myid").Str("1"), Do("GET", "mykey", "myid").Str(""), + Do("SET", "mykey", "myid", "BOUNDS", 33, -115, 33, -115, "RETURN").Str(`{"type":"Polygon","coordinates":[[[-115,33],[-115,33],[-115,33],[-115,33],[-115,33]]]}`), + Do("SET", "mykey", "myid", "BOUNDS", 33, -115, 33, -115, "RETURN", "POINT").Str("[33 -115]"), + Do("SET", "mykey", "myid", "BOUNDS", 33, -115, 33, -115, "RETURN", "BOUNDS").Str("[[33 -115] [33 -115]]"), + Do("SET", "mykey", "myid", "BOUNDS", 33, -115, 33, -115, "RETURN", "OBJECT").Str(`{"type":"Polygon","coordinates":[[[-115,33],[-115,33],[-115,33],[-115,33],[-115,33]]]}`), + Do("SET", "mykey", "myid", "BOUNDS", 33, -115, 33, -115, "RETURN", "HASH", 7).Str("9my5xp7"), + Do("DEL", "mykey", "myid").Str("1"), + Do("GET", "mykey", "myid").Str(""), + // Section: hash Do("SET", "mykey", "myid", "HASH", "9my5xp7").OK(), Do("GET", "mykey", "myid", "HASH", 7).Str("9my5xp7"), @@ -358,9 +389,16 @@ func keys_SET_test(mc *mockServer) error { Do("GET", "mykey", "myid").Str(""), Do("SET", "mykey", "myid", "HASH", "9my5xp7").JSON().OK(), + Do("SET", "mykey", "myid", "HASH", "9my5xp7", "RETURN").JSON().OK(), + Do("SET", "mykey", "myid", "HASH", "9my5xp7", "RETURN", "HASH", 7).JSON().Str(`{"ok":true,"hash":"9my5xp7"}`), + Do("DEL", "mykey", "myid").Str("1"), + Do("GET", "mykey", "myid").Str(""), + // Section: field Do("SET", "mykey", "myid", "FIELD", "f1", 33, "FIELD", "a2", 44.5, "HASH", "9my5xp7").OK(), Do("GET", "mykey", "myid", "WITHFIELDS", "HASH", 7).Str("[9my5xp7 [a2 44.5 f1 33]]"), + Do("DEL", "mykey", "myid").Str("1"), + Do("SET", "mykey", "myid", "FIELD", "f1", 33, "FIELD", "a2", 44.5, "HASH", "9my5xp7", "RETURN", "WITHFIELDS", "HASH", 7).Str("[9my5xp7 [a2 44.5 f1 33]]"), Do("FSET", "mykey", "myid", "f1", 0).Str("1"), Do("FSET", "mykey", "myid", "f1", 0).Str("0"), Do("GET", "mykey", "myid", "WITHFIELDS", "HASH", 7).Str("[9my5xp7 [a2 44.5]]"),