Merge pull request #726 from Kilowhisky/exists

Add EXIST and FEXIST command
This commit is contained in:
Josh Baker 2024-03-24 22:54:48 -07:00 committed by GitHub
commit 9221ca877b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 178 additions and 4 deletions

View File

@ -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)",

View File

@ -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)",

View File

@ -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
}

View File

@ -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()

View File

@ -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":

View File

@ -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())