Skip to content

Commit

Permalink
Go: update return types & response handlers for scan commands (valkey…
Browse files Browse the repository at this point in the history
…-io#2956)

* Go: update return types & response handlers for scan commands

Signed-off-by: TJ Zhang <[email protected]>
  • Loading branch information
tjzhang-BQ authored Jan 18, 2025
1 parent 2ca1104 commit 96894df
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 242 deletions.
186 changes: 161 additions & 25 deletions go/api/base_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -484,27 +484,83 @@ func (client *baseClient) HIncrByFloat(key string, field string, increment float
return handleFloatResponse(result)
}

func (client *baseClient) HScan(key string, cursor string) (Result[string], []Result[string], error) {
// Iterates fields of Hash types and their associated values. This definition of HSCAN command does not include the
// optional arguments of the command.
//
// See [valkey.io] for details.
//
// Parameters:
//
// key - The key of the hash.
// cursor - The cursor that points to the next iteration of results. A value of "0" indicates the start of the search.
//
// Return value:
//
// An array of the cursor and the subset of the hash held by `key`. The first element is always the `cursor`
// for the next iteration of results. The `cursor` will be `"0"` on the last iteration of the subset.
// The second element is always an array of the subset of the set held in `key`. The array in the
// second element is always a flattened series of String pairs, where the key is at even indices
// and the value is at odd indices.
//
// Example:
//
// // Assume key contains a hash {{"a": "1"}, {"b", "2"}}
// resCursor, resCollection, err = client.HScan(key, initialCursor)
// resCursor = {0 false}
// resCollection = [{a false} {1 false} {b false} {2 false}]
//
// [valkey.io]: https://valkey.io/commands/hscan/
func (client *baseClient) HScan(key string, cursor string) (string, []string, error) {
result, err := client.executeCommand(C.HScan, []string{key, cursor})
if err != nil {
return CreateNilStringResult(), nil, err
return "", nil, err
}
return handleScanResponse(result)
}

// Iterates fields of Hash types and their associated values. This definition of HSCAN includes optional arguments of the
// command.
//
// See [valkey.io] for details.
//
// Parameters:
//
// key - The key of the hash.
// cursor - The cursor that points to the next iteration of results. A value of "0" indicates the start of the search.
// options - The [api.HashScanOptions].
//
// Return value:
//
// An array of the cursor and the subset of the hash held by `key`. The first element is always the `cursor`
// for the next iteration of results. The `cursor` will be `"0"` on the last iteration of the subset.
// The second element is always an array of the subset of the set held in `key`. The array in the
// second element is always a flattened series of String pairs, where the key is at even indices
// and the value is at odd indices.
//
// Example:
//
// // Assume key contains a hash {{"a": "1"}, {"b", "2"}}
// opts := options.NewHashScanOptionsBuilder().SetMatch("a")
// resCursor, resCollection, err = client.HScan(key, initialCursor, opts)
// // resCursor = {0 false}
// // resCollection = [{a false} {1 false}]
// // The resCollection only contains the hash map entry that matches with the match option provided with the command
// // input.
//
// [valkey.io]: https://valkey.io/commands/hscan/
func (client *baseClient) HScanWithOptions(
key string,
cursor string,
options *options.HashScanOptions,
) (Result[string], []Result[string], error) {
) (string, []string, error) {
optionArgs, err := options.ToArgs()
if err != nil {
return CreateNilStringResult(), nil, err
return "", nil, err
}

result, err := client.executeCommand(C.HScan, append([]string{key, cursor}, optionArgs...))
if err != nil {
return CreateNilStringResult(), nil, err
return "", nil, err
}
return handleScanResponse(result)
}
Expand Down Expand Up @@ -735,27 +791,107 @@ func (client *baseClient) SUnion(keys []string) (map[Result[string]]struct{}, er
return handleStringSetResponse(result)
}

func (client *baseClient) SScan(key string, cursor string) (Result[string], []Result[string], error) {
// Iterates incrementally over a set.
//
// Note: When in cluster mode, all keys must map to the same hash slot.
//
// See [valkey.io] for details.
//
// Parameters:
//
// key - The key of the set.
// cursor - The cursor that points to the next iteration of results.
// A value of `"0"` indicates the start of the search.
// For Valkey 8.0 and above, negative cursors are treated like the initial cursor("0").
//
// Return value:
//
// An array of the cursor and the subset of the set held by `key`. The first element is always the `cursor` and
// for the next iteration of results. The `cursor` will be `"0"` on the last iteration of the set.
// The second element is always an array of the subset of the set held in `key`.
//
// Example:
//
// // assume "key" contains a set
// resCursor, resCol, err := client.sscan("key", "0")
// fmt.Println("Cursor: ", resCursor)
// fmt.Println("Members: ", resCol)
// for resCursor != "0" {
// resCursor, resCol, err = client.sscan("key", "0")
// fmt.Println("Cursor: ", resCursor)
// fmt.Println("Members: ", resCol)
// }
// // Output:
// // Cursor: 48
// // Members: ['3', '118', '120', '86', '76', '13', '61', '111', '55', '45']
// // Cursor: 24
// // Members: ['38', '109', '11', '119', '34', '24', '40', '57', '20', '17']
// // Cursor: 0
// // Members: ['47', '122', '1', '53', '10', '14', '80']
//
// [valkey.io]: https://valkey.io/commands/sscan/
func (client *baseClient) SScan(key string, cursor string) (string, []string, error) {
result, err := client.executeCommand(C.SScan, []string{key, cursor})
if err != nil {
return CreateNilStringResult(), nil, err
return "", nil, err
}
return handleScanResponse(result)
}

// Iterates incrementally over a set.
//
// Note: When in cluster mode, all keys must map to the same hash slot.
//
// See [valkey.io] for details.
//
// Parameters:
//
// key - The key of the set.
// cursor - The cursor that points to the next iteration of results.
// A value of `"0"` indicates the start of the search.
// For Valkey 8.0 and above, negative cursors are treated like the initial cursor("0").
// options - [options.BaseScanOptions]
//
// Return value:
//
// An array of the cursor and the subset of the set held by `key`. The first element is always the `cursor` and
// for the next iteration of results. The `cursor` will be `"0"` on the last iteration of the set.
// The second element is always an array of the subset of the set held in `key`.
//
// Example:
//
// // assume "key" contains a set
// resCursor, resCol, err := client.sscan("key", "0", opts)
// fmt.Println("Cursor: ", resCursor)
// fmt.Println("Members: ", resCol)
// for resCursor != "0" {
// opts := options.NewBaseScanOptionsBuilder().SetMatch("*")
// resCursor, resCol, err = client.sscan("key", "0", opts)
// fmt.Println("Cursor: ", resCursor)
// fmt.Println("Members: ", resCol)
// }
// // Output:
// // Cursor: 48
// // Members: ['3', '118', '120', '86', '76', '13', '61', '111', '55', '45']
// // Cursor: 24
// // Members: ['38', '109', '11', '119', '34', '24', '40', '57', '20', '17']
// // Cursor: 0
// // Members: ['47', '122', '1', '53', '10', '14', '80']
//
// [valkey.io]: https://valkey.io/commands/sscan/
func (client *baseClient) SScanWithOptions(
key string,
cursor string,
options *options.BaseScanOptions,
) (Result[string], []Result[string], error) {
) (string, []string, error) {
optionArgs, err := options.ToArgs()
if err != nil {
return CreateNilStringResult(), nil, err
return "", nil, err
}

result, err := client.executeCommand(C.SScan, append([]string{key, cursor}, optionArgs...))
if err != nil {
return CreateNilStringResult(), nil, err
return "", nil, err
}
return handleScanResponse(result)
}
Expand Down Expand Up @@ -2215,19 +2351,19 @@ func (client *baseClient) ZScore(key string, member string) (Result[float64], er
//
// // assume "key" contains a set
// resCursor, resCol, err := client.ZScan("key", "0")
// fmt.Println(resCursor.Value())
// fmt.Println(resCol.Value())
// fmt.Println(resCursor)
// fmt.Println(resCol)
// for resCursor != "0" {
// resCursor, resCol, err = client.ZScan("key", resCursor.Value())
// fmt.Println("Cursor: ", resCursor.Value())
// fmt.Println("Members: ", resCol.Value())
// resCursor, resCol, err = client.ZScan("key", resCursor)
// fmt.Println("Cursor: ", resCursor)
// fmt.Println("Members: ", resCol)
// }
//
// [valkey.io]: https://valkey.io/commands/zscan/
func (client *baseClient) ZScan(key string, cursor string) (Result[string], []Result[string], error) {
func (client *baseClient) ZScan(key string, cursor string) (string, []string, error) {
result, err := client.executeCommand(C.ZScan, []string{key, cursor})
if err != nil {
return CreateNilStringResult(), nil, err
return "", nil, err
}
return handleScanResponse(result)
}
Expand All @@ -2253,29 +2389,29 @@ func (client *baseClient) ZScan(key string, cursor string) (Result[string], []Re
// Example:
//
// resCursor, resCol, err := client.ZScanWithOptions("key", "0", options.NewBaseScanOptionsBuilder().SetMatch("*"))
// fmt.Println(resCursor.Value())
// fmt.Println(resCol.Value())
// fmt.Println(resCursor)
// fmt.Println(resCol)
// for resCursor != "0" {
// resCursor, resCol, err = client.ZScanWithOptions("key", resCursor.Value(),
// resCursor, resCol, err = client.ZScanWithOptions("key", resCursor,
// options.NewBaseScanOptionsBuilder().SetMatch("*"))
// fmt.Println("Cursor: ", resCursor.Value())
// fmt.Println("Members: ", resCol.Value())
// fmt.Println("Cursor: ", resCursor)
// fmt.Println("Members: ", resCol)
// }
//
// [valkey.io]: https://valkey.io/commands/zscan/
func (client *baseClient) ZScanWithOptions(
key string,
cursor string,
options *options.ZScanOptions,
) (Result[string], []Result[string], error) {
) (string, []string, error) {
optionArgs, err := options.ToArgs()
if err != nil {
return CreateNilStringResult(), nil, err
return "", nil, err
}

result, err := client.executeCommand(C.ZScan, append([]string{key, cursor}, optionArgs...))
if err != nil {
return CreateNilStringResult(), nil, err
return "", nil, err
}
return handleScanResponse(result)
}
Expand Down
54 changes: 2 additions & 52 deletions go/api/hash_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,57 +290,7 @@ type HashCommands interface {
// [valkey.io]: https://valkey.io/commands/hincrbyfloat/
HIncrByFloat(key string, field string, increment float64) (float64, error)

// Iterates fields of Hash types and their associated values. This definition of HSCAN command does not include the
// optional arguments of the command.
//
// See [valkey.io] for details.
//
// Parameters:
// key - The key of the hash.
// cursor - The cursor that points to the next iteration of results. A value of "0" indicates the start of the search.
//
// Return value:
// An array of the cursor and the subset of the hash held by `key`. The first element is always the `cursor`
// for the next iteration of results. The `cursor` will be `"0"` on the last iteration of the subset.
// The second element is always an array of the subset of the set held in `key`. The array in the
// second element is always a flattened series of String pairs, where the key is at even indices
// and the value is at odd indices.
//
// Example:
// // Assume key contains a hash {{"a": "1"}, {"b", "2"}}
// resCursor, resCollection, err = client.HScan(key, initialCursor)
// // resCursor = {0 false}
// // resCollection = [{a false} {1 false} {b false} {2 false}]
//
// [valkey.io]: https://valkey.io/commands/hscan/
HScan(key string, cursor string) (Result[string], []Result[string], error)
HScan(key string, cursor string) (string, []string, error)

// Iterates fields of Hash types and their associated values. This definition of HSCAN includes optional arguments of the
// command.
//
// See [valkey.io] for details.
//
// Parameters:
// key - The key of the hash.
// cursor - The cursor that points to the next iteration of results. A value of "0" indicates the start of the search.
// options - The [api.HashScanOptions].
//
// Return value:
// An array of the cursor and the subset of the hash held by `key`. The first element is always the `cursor`
// for the next iteration of results. The `cursor` will be `"0"` on the last iteration of the subset.
// The second element is always an array of the subset of the set held in `key`. The array in the
// second element is always a flattened series of String pairs, where the key is at even indices
// and the value is at odd indices.
//
// Example:
// // Assume key contains a hash {{"a": "1"}, {"b", "2"}}
// opts := options.NewHashScanOptionsBuilder().SetMatch("a")
// resCursor, resCollection, err = client.HScan(key, initialCursor, opts)
// // resCursor = {0 false}
// // resCollection = [{a false} {1 false}]
// // The resCollection only contains the hash map entry that matches with the match option provided with the command
// // input.
//
// [valkey.io]: https://valkey.io/commands/hscan/
HScanWithOptions(key string, cursor string, options *options.HashScanOptions) (Result[string], []Result[string], error)
HScanWithOptions(key string, cursor string, options *options.HashScanOptions) (string, []string, error)
}
22 changes: 10 additions & 12 deletions go/api/response_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,40 +461,38 @@ func handleKeyWithMemberAndScoreResponse(response *C.struct_CommandResponse) (Re
return CreateKeyWithMemberAndScoreResult(KeyWithMemberAndScore{key, member, score}), nil
}

func handleScanResponse(
response *C.struct_CommandResponse,
) (Result[string], []Result[string], error) {
func handleScanResponse(response *C.struct_CommandResponse) (string, []string, error) {
defer C.free_command_response(response)

typeErr := checkResponseType(response, C.Array, false)
if typeErr != nil {
return CreateNilStringResult(), nil, typeErr
return "", nil, typeErr
}

slice, err := parseArray(response)
if err != nil {
return CreateNilStringResult(), nil, err
return "", nil, err
}

if arr, ok := slice.([]interface{}); ok {
resCollection, err := convertToResultStringArray(arr[1].([]interface{}))
resCollection, err := convertToStringArray(arr[1].([]interface{}))
if err != nil {
return CreateNilStringResult(), nil, err
return "", nil, err
}
return CreateStringResult(arr[0].(string)), resCollection, nil
return arr[0].(string), resCollection, nil
}

return CreateNilStringResult(), nil, err
return "", nil, err
}

func convertToResultStringArray(input []interface{}) ([]Result[string], error) {
result := make([]Result[string], len(input))
func convertToStringArray(input []interface{}) ([]string, error) {
result := make([]string, len(input))
for i, v := range input {
str, ok := v.(string)
if !ok {
return nil, fmt.Errorf("element at index %d is not a string: %v", i, v)
}
result[i] = CreateStringResult(str)
result[i] = str
}
return result, nil
}
Expand Down
Loading

0 comments on commit 96894df

Please sign in to comment.