mirror of https://github.com/tidwall/tile38.git
Merge branch 'master' into exists
This commit is contained in:
commit
9e869e04c3
|
@ -144,6 +144,11 @@ To set a field when an object already exists:
|
||||||
> fset fleet truck1 speed 90
|
> fset fleet truck1 speed 90
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To get a field when an object already exists:
|
||||||
|
```
|
||||||
|
> fget fleet truck1 speed
|
||||||
|
```
|
||||||
|
|
||||||
## Searching
|
## Searching
|
||||||
|
|
||||||
Tile38 has support to search for objects and points that are within or intersects other objects. All object types can be searched including Polygons, MultiPolygons, GeometryCollections, etc.
|
Tile38 has support to search for objects and points that are within or intersects other objects. All object types can be searched including Polygons, MultiPolygons, GeometryCollections, etc.
|
||||||
|
|
|
@ -232,6 +232,26 @@
|
||||||
"since": "1.0.0",
|
"since": "1.0.0",
|
||||||
"group": "keys"
|
"group": "keys"
|
||||||
},
|
},
|
||||||
|
"FGET": {
|
||||||
|
"summary": "Gets the value for the field of an id",
|
||||||
|
"complexity": "O(1)",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "field",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"since": "1.33.0",
|
||||||
|
"group": "keys"
|
||||||
|
},
|
||||||
"BOUNDS": {
|
"BOUNDS": {
|
||||||
"summary": "Get the combined bounds of all the objects in a key",
|
"summary": "Get the combined bounds of all the objects in a key",
|
||||||
"complexity": "O(1)",
|
"complexity": "O(1)",
|
||||||
|
|
|
@ -398,6 +398,26 @@ var commandsJSON = `{
|
||||||
"since": "1.0.0",
|
"since": "1.0.0",
|
||||||
"group": "keys"
|
"group": "keys"
|
||||||
},
|
},
|
||||||
|
"FGET": {
|
||||||
|
"summary": "Gets the value for the field of an id",
|
||||||
|
"complexity": "O(1)",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "field",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"since": "1.33.0",
|
||||||
|
"group": "keys"
|
||||||
|
},
|
||||||
"BOUNDS": {
|
"BOUNDS": {
|
||||||
"summary": "Get the combined bounds of all the objects in a key",
|
"summary": "Get the combined bounds of all the objects in a key",
|
||||||
"complexity": "O(1)",
|
"complexity": "O(1)",
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -112,5 +112,5 @@ require (
|
||||||
google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
|
||||||
google.golang.org/protobuf v1.31.0 // indirect
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -732,8 +732,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|
|
@ -907,6 +907,46 @@ func (s *Server) cmdFSET(msg *Message) (resp.Value, commandDetails, error) {
|
||||||
return res, d, nil
|
return res, d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FGET key id field
|
||||||
|
func (s *Server) cmdFGET(msg *Message) (resp.Value, error) {
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
// >> Args
|
||||||
|
|
||||||
|
args := msg.Args
|
||||||
|
|
||||||
|
if len(args) < 4 {
|
||||||
|
return retrerr(errInvalidNumberOfArguments)
|
||||||
|
}
|
||||||
|
key, id, field := args[1], args[2], args[3]
|
||||||
|
|
||||||
|
// >> Operation
|
||||||
|
|
||||||
|
col, _ := s.cols.Get(key)
|
||||||
|
if col == nil {
|
||||||
|
return retrerr(errKeyNotFound)
|
||||||
|
}
|
||||||
|
o := col.Get(id)
|
||||||
|
if o == nil {
|
||||||
|
return retrerr(errIDNotFound)
|
||||||
|
}
|
||||||
|
f := o.Fields().Get(field)
|
||||||
|
|
||||||
|
// >> Response
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
switch msg.OutputType {
|
||||||
|
case JSON:
|
||||||
|
buf.WriteString(`{"ok":true`)
|
||||||
|
buf.WriteString(`,"value":` + f.Value().JSON())
|
||||||
|
buf.WriteString(`,"elapsed":"` + time.Since(start).String() + "\"}")
|
||||||
|
return resp.StringValue(buf.String()), nil
|
||||||
|
case RESP:
|
||||||
|
return resp.StringValue(f.Value().Data()), nil
|
||||||
|
}
|
||||||
|
return NOMessage, nil
|
||||||
|
}
|
||||||
|
|
||||||
// EXPIRE key id seconds
|
// EXPIRE key id seconds
|
||||||
func (s *Server) cmdEXPIRE(msg *Message) (resp.Value, commandDetails, error) {
|
func (s *Server) cmdEXPIRE(msg *Message) (resp.Value, commandDetails, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
|
@ -665,6 +665,8 @@ func (s *Server) commandInScript(msg *Message) (
|
||||||
res, err = s.cmdBOUNDS(msg)
|
res, err = s.cmdBOUNDS(msg)
|
||||||
case "get":
|
case "get":
|
||||||
res, err = s.cmdGET(msg)
|
res, err = s.cmdGET(msg)
|
||||||
|
case "fget":
|
||||||
|
res, err = s.cmdFGET(msg)
|
||||||
case "jget":
|
case "jget":
|
||||||
res, err = s.cmdJget(msg)
|
res, err = s.cmdJget(msg)
|
||||||
case "jset":
|
case "jset":
|
||||||
|
@ -739,7 +741,7 @@ func (s *Server) luaTile38AtomicRW(msg *Message) (resp.Value, error) {
|
||||||
return resp.NullValue(), errReadOnly
|
return resp.NullValue(), errReadOnly
|
||||||
}
|
}
|
||||||
case "get", "keys", "scan", "nearby", "within", "intersects", "hooks", "search",
|
case "get", "keys", "scan", "nearby", "within", "intersects", "hooks", "search",
|
||||||
"ttl", "bounds", "server", "info", "type", "jget", "exists", "fexists", "test":
|
"ttl", "bounds", "server", "info", "type", "jget", "fget", "exists", "fexists", "test":
|
||||||
// read operations
|
// read operations
|
||||||
if s.config.followHost() != "" && !s.fcuponce {
|
if s.config.followHost() != "" && !s.fcuponce {
|
||||||
return resp.NullValue(), errCatchingUp
|
return resp.NullValue(), errCatchingUp
|
||||||
|
@ -792,7 +794,7 @@ func (s *Server) luaTile38AtomicRO(msg *Message) (resp.Value, error) {
|
||||||
return resp.NullValue(), errReadOnly
|
return resp.NullValue(), errReadOnly
|
||||||
|
|
||||||
case "get", "keys", "scan", "nearby", "within", "intersects", "hooks", "search",
|
case "get", "keys", "scan", "nearby", "within", "intersects", "hooks", "search",
|
||||||
"ttl", "bounds", "server", "info", "type", "jget", "exists", "fexists", "test":
|
"ttl", "bounds", "server", "info", "type", "jget", "fget", "exists", "fexists", "test":
|
||||||
// read operations
|
// read operations
|
||||||
if s.config.followHost() != "" && !s.fcuponce {
|
if s.config.followHost() != "" && !s.fcuponce {
|
||||||
return resp.NullValue(), errCatchingUp
|
return resp.NullValue(), errCatchingUp
|
||||||
|
@ -843,7 +845,7 @@ func (s *Server) luaTile38NonAtomic(msg *Message) (resp.Value, error) {
|
||||||
return resp.NullValue(), errReadOnly
|
return resp.NullValue(), errReadOnly
|
||||||
}
|
}
|
||||||
case "get", "keys", "scan", "nearby", "within", "intersects", "hooks", "search",
|
case "get", "keys", "scan", "nearby", "within", "intersects", "hooks", "search",
|
||||||
"ttl", "bounds", "server", "info", "type", "jget", "exists", "fexists", "test":
|
"ttl", "bounds", "server", "info", "type", "jget", "fget", "exists", "fexists", "test":
|
||||||
// read operations
|
// read operations
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
defer s.mu.RUnlock()
|
defer s.mu.RUnlock()
|
||||||
|
|
|
@ -1024,7 +1024,7 @@ func (s *Server) handleInputCommand(client *Client, msg *Message) error {
|
||||||
}
|
}
|
||||||
case "get", "keys", "scan", "nearby", "within", "intersects", "hooks",
|
case "get", "keys", "scan", "nearby", "within", "intersects", "hooks",
|
||||||
"chans", "search", "ttl", "bounds", "server", "info", "type", "jget",
|
"chans", "search", "ttl", "bounds", "server", "info", "type", "jget",
|
||||||
"evalro", "evalrosha", "healthz", "role", "exists", "fexists":
|
"evalro", "evalrosha", "healthz", "role", "fget", "exists", "fexists":
|
||||||
// read operations
|
// read operations
|
||||||
|
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
|
@ -1227,6 +1227,8 @@ func (s *Server) command(msg *Message, client *Client) (
|
||||||
res, err = s.cmdBOUNDS(msg)
|
res, err = s.cmdBOUNDS(msg)
|
||||||
case "get":
|
case "get":
|
||||||
res, err = s.cmdGET(msg)
|
res, err = s.cmdGET(msg)
|
||||||
|
case "fget":
|
||||||
|
res, err = s.cmdFGET(msg)
|
||||||
case "jget":
|
case "jget":
|
||||||
res, err = s.cmdJget(msg)
|
res, err = s.cmdJget(msg)
|
||||||
case "jset":
|
case "jset":
|
||||||
|
|
|
@ -19,6 +19,7 @@ func subTestKeys(g *testGroup) {
|
||||||
g.regSubTest("RENAMENX", keys_RENAMENX_test)
|
g.regSubTest("RENAMENX", keys_RENAMENX_test)
|
||||||
g.regSubTest("EXPIRE", keys_EXPIRE_test)
|
g.regSubTest("EXPIRE", keys_EXPIRE_test)
|
||||||
g.regSubTest("FSET", keys_FSET_test)
|
g.regSubTest("FSET", keys_FSET_test)
|
||||||
|
g.regSubTest("FGET", keys_FGET_test)
|
||||||
g.regSubTest("GET", keys_GET_test)
|
g.regSubTest("GET", keys_GET_test)
|
||||||
g.regSubTest("KEYS", keys_KEYS_test)
|
g.regSubTest("KEYS", keys_KEYS_test)
|
||||||
g.regSubTest("PERSIST", keys_PERSIST_test)
|
g.regSubTest("PERSIST", keys_PERSIST_test)
|
||||||
|
@ -203,6 +204,25 @@ func keys_FSET_test(mc *mockServer) error {
|
||||||
Do("FSET", "mykey", "myid", "f2", 0).JSON().OK(),
|
Do("FSET", "mykey", "myid", "f2", 0).JSON().OK(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
func keys_FGET_test(mc *mockServer) error {
|
||||||
|
return mc.DoBatch(
|
||||||
|
Do("SET", "mykey", "myid", "HASH", "9my5xp7").OK(),
|
||||||
|
Do("GET", "mykey", "myid", "WITHFIELDS", "HASH", 7).Str("[9my5xp7]"),
|
||||||
|
Do("FSET", "mykey", "myid", "f1", 105.6).Str("1"),
|
||||||
|
Do("FGET", "mykey", "myid", "f1").Str("105.6"),
|
||||||
|
Do("FSET", "mykey", "myid", "f1", 1.1, "f2", 2.2).Str("2"),
|
||||||
|
Do("FGET", "mykey", "myid", "f2").Str("2.2"),
|
||||||
|
Do("FGET", "mykey", "myid", "f1").Str("1.1"),
|
||||||
|
Do("FGET", "mykey", "myid", "f1").JSON().Str(`{"ok":true,"value":1.1}`),
|
||||||
|
Do("FSET", "mykey", "myid", "f3", "a").Str("1"),
|
||||||
|
Do("FGET", "mykey", "myid", "f3").Str("a"),
|
||||||
|
Do("FGET", "mykey", "myid", "f4").Str("0"),
|
||||||
|
Do("FGET", "mykey", "myid", "f4").JSON().Str(`{"ok":true,"value":0}`),
|
||||||
|
Do("FGET", "mykey", "myid").Err("wrong number of arguments for 'fget' command"),
|
||||||
|
Do("FGET", "mykey2", "myid", "a", "b").Err("key not found"),
|
||||||
|
Do("FGET", "mykey", "myid2", "a", "b").Err("id not found"),
|
||||||
|
)
|
||||||
|
}
|
||||||
func keys_GET_test(mc *mockServer) error {
|
func keys_GET_test(mc *mockServer) error {
|
||||||
return mc.DoBatch(
|
return mc.DoBatch(
|
||||||
Do("SET", "mykey", "myid", "STRING", "value").OK(),
|
Do("SET", "mykey", "myid", "STRING", "value").OK(),
|
||||||
|
|
Loading…
Reference in New Issue