From 33ca13c778ea30946a6b6a3682cdf2f7595fb063 Mon Sep 17 00:00:00 2001 From: Ankush Chavan Date: Thu, 14 May 2026 20:19:35 +0530 Subject: [PATCH] feat(cmd): implement EXISTS command Signed-off-by: Ankush Chavan --- README.md | 1 + cmd/exists.go | 22 ++++++++++++++++++ cmd/exists_test.go | 37 ++++++++++++++++++++++++++++++ common/constants.go | 1 + docs/README.md | 1 + docs/exists.md | 50 +++++++++++++++++++++++++++++++++++++++++ exc/command_executor.go | 2 ++ 7 files changed, 114 insertions(+) create mode 100644 cmd/exists.go create mode 100644 cmd/exists_test.go create mode 100644 docs/exists.md diff --git a/README.md b/README.md index bd3c9b2..1331ca8 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,4 @@ See the [Usage Guide](./docs/usage.md) for installation, connecting, and a walkt | [KEYS](./docs/keys.md) | Find keys matching a pattern | | [CONFIG](./docs/config.md) | Read and modify server configuration | | [TYPE](./docs/type.md) | Return the type of the value stored at a key | +| [EXISTS](./docs/exists.md) | Check if one or more keys exist | diff --git a/cmd/exists.go b/cmd/exists.go new file mode 100644 index 0000000..b1dfca0 --- /dev/null +++ b/cmd/exists.go @@ -0,0 +1,22 @@ +package cmd + +import ( + "HelixDB/common" +) + +// Exists returns the number of the given keys that exist in the store. +// If the same key is provided multiple times it is counted multiple times. +// Expired keys are treated as non-existent. +func Exists(command common.Cmd) ([]byte, error) { + if len(command.Args) < 1 { + err := common.WrongNumberOfArgsError(command.Name) + return common.RespError(err.Error()), err + } + count := 0 + for _, key := range command.Args { + if _, err := GetValueFromMemory(key); err == nil { + count++ + } + } + return common.RespInteger(count), nil +} diff --git a/cmd/exists_test.go b/cmd/exists_test.go new file mode 100644 index 0000000..00f7c5d --- /dev/null +++ b/cmd/exists_test.go @@ -0,0 +1,37 @@ +package cmd + +import ( + "HelixDB/common" + "errors" + "reflect" + "testing" +) + +func TestExists(t *testing.T) { + _, _ = Set(common.Cmd{Name: "SET", Args: []string{"ex_key1", "value1"}}) + _, _ = Set(common.Cmd{Name: "SET", Args: []string{"ex_key2", "value2"}}) + + tests := []struct { + command common.Cmd + want []byte + wantErr error + }{ + // Single existing key + {common.Cmd{Name: "EXISTS", Args: []string{"ex_key1"}}, common.RespInteger(1), nil}, + // Single non-existent key + {common.Cmd{Name: "EXISTS", Args: []string{"no_such_key"}}, common.RespInteger(0), nil}, + // Multiple keys, all exist + {common.Cmd{Name: "EXISTS", Args: []string{"ex_key1", "ex_key2"}}, common.RespInteger(2), nil}, + // Multiple keys, some missing + {common.Cmd{Name: "EXISTS", Args: []string{"ex_key1", "no_such_key"}}, common.RespInteger(1), nil}, + // Duplicate key counts multiple times + {common.Cmd{Name: "EXISTS", Args: []string{"ex_key1", "ex_key1"}}, common.RespInteger(2), nil}, + // Wrong number of arguments + {common.Cmd{Name: "EXISTS"}, common.RespError("wrong number of arguments for 'exists' command"), common.ErrWrongNumberOfArgs}, + } + for _, test := range tests { + if got, gotErr := Exists(test.command); !reflect.DeepEqual(got, test.want) || !errors.Is(gotErr, test.wantErr) { + t.Errorf("Exists(%v) = %v, %v; want %v, %v", test.command, got, gotErr, test.want, test.wantErr) + } + } +} diff --git a/common/constants.go b/common/constants.go index 54654e7..18479f5 100644 --- a/common/constants.go +++ b/common/constants.go @@ -14,6 +14,7 @@ const Del = "DEL" const Keys = "KEYS" const Config = "CONFIG" const Type = "TYPE" +const Exists = "EXISTS" // Command Args // Expiration Args diff --git a/docs/README.md b/docs/README.md index 0f78db6..dcf41bc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -17,3 +17,4 @@ | [KEYS](./keys.md) | Find keys matching a pattern | | [CONFIG](./config.md) | Read and modify server configuration | | [TYPE](./type.md) | Return the type of the value stored at a key | +| [EXISTS](./exists.md) | Check if one or more keys exist | diff --git a/docs/exists.md b/docs/exists.md new file mode 100644 index 0000000..8482b71 --- /dev/null +++ b/docs/exists.md @@ -0,0 +1,50 @@ +# EXISTS + +Returns the number of the given keys that exist in the store. Expired keys are treated as non-existent. + +If the same key is provided multiple times it is counted multiple times. + +## Syntax + +``` +EXISTS key [key ...] +``` + +## Arguments + +| Argument | Type | Required | Description | +|----------|--------|----------|------------------------------| +| `key` | string | Yes | One or more keys to check. | + +## Return value + +The number of keys that exist. Ranges from `0` (none exist) to the total number of key arguments supplied. + +## Errors + +- `wrong number of arguments for 'exists' command` — when called with no arguments. + +## Examples + +``` +> SET a 1 +OK + +> SET b 2 +OK + +> EXISTS a +1 + +> EXISTS a b +2 + +> EXISTS a missing +1 + +> EXISTS missing +0 + +> EXISTS a a +2 +``` diff --git a/exc/command_executor.go b/exc/command_executor.go index 449915c..347437a 100644 --- a/exc/command_executor.go +++ b/exc/command_executor.go @@ -30,6 +30,8 @@ func ExecuteCommand(command common.Cmd) ([]byte, error) { return cmd.ConfigCmd(command) case common.Type: return cmd.Type(command) + case common.Exists: + return cmd.Exists(command) } return []byte("-unsupported command\r\n"), UnsupportedCommand }