mirror of https://github.com/tidwall/tile38.git
Merge pull request #726 from Kilowhisky/exists
Add EXIST and FEXIST command
This commit is contained in:
commit
9221ca877b
|
@ -148,6 +148,42 @@
|
||||||
"since": "1.0.0",
|
"since": "1.0.0",
|
||||||
"group": "keys"
|
"group": "keys"
|
||||||
},
|
},
|
||||||
|
"EXISTS": {
|
||||||
|
"summary": "Checks to see if a id exists",
|
||||||
|
"complexity": "O(1)",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"since": "1.33.0",
|
||||||
|
"group": "keys"
|
||||||
|
},
|
||||||
|
"FEXISTS": {
|
||||||
|
"summary": "Checks to see if a field exists on a id",
|
||||||
|
"complexity": "O(1)",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "field",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"since": "1.33.0",
|
||||||
|
"group": "keys"
|
||||||
|
},
|
||||||
"PERSIST": {
|
"PERSIST": {
|
||||||
"summary": "Remove the existing timeout on an id",
|
"summary": "Remove the existing timeout on an id",
|
||||||
"complexity": "O(1)",
|
"complexity": "O(1)",
|
||||||
|
|
|
@ -314,6 +314,42 @@ var commandsJSON = `{
|
||||||
"since": "1.0.0",
|
"since": "1.0.0",
|
||||||
"group": "keys"
|
"group": "keys"
|
||||||
},
|
},
|
||||||
|
"EXISTS": {
|
||||||
|
"summary": "Checks to see if a id exists",
|
||||||
|
"complexity": "O(1)",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"since": "1.33.0",
|
||||||
|
"group": "keys"
|
||||||
|
},
|
||||||
|
"FEXISTS": {
|
||||||
|
"summary": "Checks to see if a field exists on a id",
|
||||||
|
"complexity": "O(1)",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "field",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"since": "1.33.0",
|
||||||
|
"group": "keys"
|
||||||
|
},
|
||||||
"PERSIST": {
|
"PERSIST": {
|
||||||
"summary": "Remove the existing timeout on an id",
|
"summary": "Remove the existing timeout on an id",
|
||||||
"complexity": "O(1)",
|
"complexity": "O(1)",
|
||||||
|
|
|
@ -1120,3 +1120,72 @@ func (s *Server) cmdTTL(msg *Message) (resp.Value, error) {
|
||||||
}
|
}
|
||||||
return resp.IntegerValue(int(ttl)), nil
|
return resp.IntegerValue(int(ttl)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EXISTS key id
|
||||||
|
func (s *Server) cmdEXISTS(msg *Message) (resp.Value, error) {
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
// >> Args
|
||||||
|
|
||||||
|
args := msg.Args
|
||||||
|
if len(args) != 3 {
|
||||||
|
return retrerr(errInvalidNumberOfArguments)
|
||||||
|
}
|
||||||
|
key, id := args[1], args[2]
|
||||||
|
|
||||||
|
// >> Operation
|
||||||
|
|
||||||
|
col, _ := s.cols.Get(key)
|
||||||
|
if col == nil {
|
||||||
|
return retrerr(errKeyNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
o := col.Get(id)
|
||||||
|
exists := o != nil
|
||||||
|
|
||||||
|
// >> Response
|
||||||
|
|
||||||
|
if msg.OutputType == JSON {
|
||||||
|
return resp.SimpleStringValue(
|
||||||
|
`{"ok":true,"exists":` + strconv.FormatBool(exists) + `,"elapsed":"` +
|
||||||
|
time.Since(start).String() + "\"}"), nil
|
||||||
|
}
|
||||||
|
return resp.BoolValue(exists), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FEXISTS key id field
|
||||||
|
func (s *Server) cmdFEXISTS(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)
|
||||||
|
exists := f.Name() != ""
|
||||||
|
|
||||||
|
// >> Response
|
||||||
|
|
||||||
|
if msg.OutputType == JSON {
|
||||||
|
return resp.SimpleStringValue(
|
||||||
|
`{"ok":true,"exists":` + strconv.FormatBool(exists) + `,"elapsed":"` +
|
||||||
|
time.Since(start).String() + "\"}"), nil
|
||||||
|
}
|
||||||
|
return resp.BoolValue(exists), nil
|
||||||
|
}
|
||||||
|
|
|
@ -677,6 +677,10 @@ func (s *Server) commandInScript(msg *Message) (
|
||||||
res, err = s.cmdTYPE(msg)
|
res, err = s.cmdTYPE(msg)
|
||||||
case "keys":
|
case "keys":
|
||||||
res, err = s.cmdKEYS(msg)
|
res, err = s.cmdKEYS(msg)
|
||||||
|
case "exists":
|
||||||
|
res, err = s.cmdEXISTS(msg)
|
||||||
|
case "fexists":
|
||||||
|
res, err = s.cmdFEXISTS(msg)
|
||||||
case "test":
|
case "test":
|
||||||
res, err = s.cmdTEST(msg)
|
res, err = s.cmdTEST(msg)
|
||||||
case "server":
|
case "server":
|
||||||
|
@ -737,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", "fget", "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
|
||||||
|
@ -790,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", "fget", "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
|
||||||
|
@ -841,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", "fget", "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", "fget":
|
"evalro", "evalrosha", "healthz", "role", "fget", "exists", "fexists":
|
||||||
// read operations
|
// read operations
|
||||||
|
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
|
@ -1239,6 +1239,10 @@ func (s *Server) command(msg *Message, client *Client) (
|
||||||
res, err = s.cmdTYPE(msg)
|
res, err = s.cmdTYPE(msg)
|
||||||
case "keys":
|
case "keys":
|
||||||
res, err = s.cmdKEYS(msg)
|
res, err = s.cmdKEYS(msg)
|
||||||
|
case "exists":
|
||||||
|
res, err = s.cmdEXISTS(msg)
|
||||||
|
case "fexists":
|
||||||
|
res, err = s.cmdFEXISTS(msg)
|
||||||
case "output":
|
case "output":
|
||||||
res, err = s.cmdOUTPUT(msg)
|
res, err = s.cmdOUTPUT(msg)
|
||||||
case "aof":
|
case "aof":
|
||||||
|
|
|
@ -26,6 +26,8 @@ func subTestKeys(g *testGroup) {
|
||||||
g.regSubTest("SET", keys_SET_test)
|
g.regSubTest("SET", keys_SET_test)
|
||||||
g.regSubTest("STATS", keys_STATS_test)
|
g.regSubTest("STATS", keys_STATS_test)
|
||||||
g.regSubTest("TTL", keys_TTL_test)
|
g.regSubTest("TTL", keys_TTL_test)
|
||||||
|
g.regSubTest("EXIST", keys_EXISTS_test)
|
||||||
|
g.regSubTest("FEXIST", keys_FEXISTS_test)
|
||||||
g.regSubTest("SET EX", keys_SET_EX_test)
|
g.regSubTest("SET EX", keys_SET_EX_test)
|
||||||
g.regSubTest("PDEL", keys_PDEL_test)
|
g.regSubTest("PDEL", keys_PDEL_test)
|
||||||
g.regSubTest("FIELDS", keys_FIELDS_test)
|
g.regSubTest("FIELDS", keys_FIELDS_test)
|
||||||
|
@ -429,6 +431,29 @@ func keys_TTL_test(mc *mockServer) error {
|
||||||
Do("TTL", "mykey", "myid2").JSON().Err("id not found"),
|
Do("TTL", "mykey", "myid2").JSON().Err("id not found"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
func keys_EXISTS_test(mc *mockServer) error {
|
||||||
|
return mc.DoBatch(
|
||||||
|
Do("SET", "mykey", "myid", "STRING", "value").OK(),
|
||||||
|
Do("EXISTS", "mykey", "myid").Str("1"),
|
||||||
|
Do("EXISTS", "mykey", "myid").JSON().Str(`{"ok":true,"exists":true}`),
|
||||||
|
Do("EXISTS", "mykey", "myid2").Str("0"),
|
||||||
|
Do("EXISTS", "mykey", "myid2").JSON().Str(`{"ok":true,"exists":false}`),
|
||||||
|
Do("EXISTS", "mykey").Err("wrong number of arguments for 'exists' command"),
|
||||||
|
Do("EXISTS", "mykey2", "myid").JSON().Err("key not found"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
func keys_FEXISTS_test(mc *mockServer) error {
|
||||||
|
return mc.DoBatch(
|
||||||
|
Do("SET", "mykey", "myid", "FIELD", "f1", "123", "STRING", "value").OK(),
|
||||||
|
Do("FEXISTS", "mykey", "myid", "f1").Str("1"),
|
||||||
|
Do("FEXISTS", "mykey", "myid", "f1").JSON().Str(`{"ok":true,"exists":true}`),
|
||||||
|
Do("FEXISTS", "mykey", "myid", "f2").Str("0"),
|
||||||
|
Do("FEXISTS", "mykey", "myid", "f2").JSON().Str(`{"ok":true,"exists":false}`),
|
||||||
|
Do("FEXISTS", "mykey", "myid").Err("wrong number of arguments for 'fexists' command"),
|
||||||
|
Do("FEXISTS", "mykey2", "myid", "f2").JSON().Err("key not found"),
|
||||||
|
Do("FEXISTS", "mykey", "myid2", "f2").JSON().Err("id not found"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func keys_SET_EX_test(mc *mockServer) (err error) {
|
func keys_SET_EX_test(mc *mockServer) (err error) {
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
Loading…
Reference in New Issue