Skip to content

Commit

Permalink
Update branch and fix conflict
Browse files Browse the repository at this point in the history
Signed-off-by: EdricCua <[email protected]>
  • Loading branch information
EdricCua committed Jan 6, 2025
2 parents d592a41 + 8b904d4 commit 809df46
Show file tree
Hide file tree
Showing 14 changed files with 505 additions and 54 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#### Changes

* Go: Add `HScan` command ([#2917](https://github.com/valkey-io/valkey-glide/pull/2917))
* Java, Node, Python: Add transaction commands for JSON module ([#2862](https://github.com/valkey-io/valkey-glide/pull/2862))
* Go: Add HINCRBY command ([#2847](https://github.com/valkey-io/valkey-glide/pull/2847))
* Go: Add HINCRBYFLOAT command ([#2846](https://github.com/valkey-io/valkey-glide/pull/2846))
Expand All @@ -13,6 +15,7 @@
* Go: Add `ZPopMin` and `ZPopMax` ([#2850](https://github.com/valkey-io/valkey-glide/pull/2850))
* Java: Add binary version of `ZRANK WITHSCORE` ([#2896](https://github.com/valkey-io/valkey-glide/pull/2896))
* Go: Add `ZCARD` ([#2838](https://github.com/valkey-io/valkey-glide/pull/2838))
* Go: Add `BZPopMin` ([#2849](https://github.com/valkey-io/valkey-glide/pull/2849))

#### Breaking Changes

Expand Down
3 changes: 1 addition & 2 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ yanked = "deny"
# A list of advisory IDs to ignore. Note that ignored advisories will still
# output a note when they are encountered.
ignore = [
# Unmaintained dependency error that needs more attention due to nested dependencies
"RUSTSEC-2024-0370",
#"RUSTSEC-0000-0000",
]
# Threshold for security vulnerabilities, any vulnerability with a CVSS score
# lower than the range specified will be ignored. Note that ignored advisories
Expand Down
38 changes: 36 additions & 2 deletions go/api/base_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,31 @@ func (client *baseClient) HIncrByFloat(key string, field string, increment float
return handleDoubleResponse(result)
}

func (client *baseClient) HScan(key string, cursor string) (Result[string], []Result[string], error) {
result, err := client.executeCommand(C.HScan, []string{key, cursor})
if err != nil {
return CreateNilStringResult(), nil, err
}
return handleScanResponse(result)
}

func (client *baseClient) HScanWithOptions(
key string,
cursor string,
options *options.HashScanOptions,
) (Result[string], []Result[string], error) {
optionArgs, err := options.ToArgs()
if err != nil {
return CreateNilStringResult(), nil, err
}

result, err := client.executeCommand(C.HScan, append([]string{key, cursor}, optionArgs...))
if err != nil {
return CreateNilStringResult(), nil, err
}
return handleScanResponse(result)
}

func (client *baseClient) LPush(key string, elements []string) (Result[int64], error) {
result, err := client.executeCommand(C.LPush, append([]string{key}, elements...))
if err != nil {
Expand Down Expand Up @@ -721,9 +746,9 @@ func (client *baseClient) SScan(key string, cursor string) (Result[string], []Re
func (client *baseClient) SScanWithOptions(
key string,
cursor string,
options *BaseScanOptions,
options *options.BaseScanOptions,
) (Result[string], []Result[string], error) {
optionArgs, err := options.toArgs()
optionArgs, err := options.ToArgs()
if err != nil {
return CreateNilStringResult(), nil, err
}
Expand Down Expand Up @@ -1442,6 +1467,15 @@ func (client *baseClient) ZCard(key string) (Result[int64], error) {
return handleLongResponse(result)
}

func (client *baseClient) BZPopMin(keys []string, timeoutSecs float64) (Result[KeyWithMemberAndScore], error) {
result, err := client.executeCommand(C.BZPopMin, append(keys, utils.FloatToString(timeoutSecs)))
if err != nil {
return CreateNilKeyWithMemberAndScoreResult(), err
}

return handleKeyWithMemberAndScoreResponse(result)
}

func (client *baseClient) Echo(message string) (Result[string], error) {
result, err := client.executeCommand(C.Echo, []string{message})
if err != nil {
Expand Down
43 changes: 0 additions & 43 deletions go/api/command_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,46 +278,3 @@ func (listDirection ListDirection) toString() (string, error) {
return "", &RequestError{"Invalid list direction"}
}
}

// This base option struct represents the common set of optional arguments for the SCAN family of commands.
// Concrete implementations of this class are tied to specific SCAN commands (`SCAN`, `SSCAN`).
type BaseScanOptions struct {
match string
count int64
}

func NewBaseScanOptionsBuilder() *BaseScanOptions {
return &BaseScanOptions{}
}

// The match filter is applied to the result of the command and will only include
// strings that match the pattern specified. If the sorted set is large enough for scan commands to return
// only a subset of the sorted set then there could be a case where the result is empty although there are
// items that match the pattern specified. This is due to the default `COUNT` being `10` which indicates
// that it will only fetch and match `10` items from the list.
func (scanOptions *BaseScanOptions) SetMatch(m string) *BaseScanOptions {
scanOptions.match = m
return scanOptions
}

// `COUNT` is a just a hint for the command for how many elements to fetch from the
// sorted set. `COUNT` could be ignored until the sorted set is large enough for the `SCAN` commands to
// represent the results as compact single-allocation packed encoding.
func (scanOptions *BaseScanOptions) SetCount(c int64) *BaseScanOptions {
scanOptions.count = c
return scanOptions
}

func (opts *BaseScanOptions) toArgs() ([]string, error) {
args := []string{}
var err error
if opts.match != "" {
args = append(args, MatchKeyword, opts.match)
}

if opts.count != 0 {
args = append(args, CountKeyword, strconv.FormatInt(opts.count, 10))
}

return args, err
}
56 changes: 56 additions & 0 deletions go/api/hash_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

package api

import "github.com/valkey-io/valkey-glide/go/glide/api/options"

// Supports commands and transactions for the "Hash" group of commands for standalone and cluster clients.
//
// See [valkey.io] for details.
Expand Down Expand Up @@ -292,4 +294,58 @@ type HashCommands interface {
//
// [valkey.io]: https://valkey.io/commands/hincrbyfloat/
HIncrByFloat(key string, field string, increment float64) (Result[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)

// 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)
}
54 changes: 54 additions & 0 deletions go/api/options/base_scan_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0

package options

import (
"strconv"
)

// This base option struct represents the common set of optional arguments for the SCAN family of commands.
// Concrete implementations of this class are tied to specific SCAN commands (`SCAN`, `SSCAN`, `HSCAN`).
type BaseScanOptions struct {
match string
count int64
}

func NewBaseScanOptionsBuilder() *BaseScanOptions {
return &BaseScanOptions{}
}

/*
The match filter is applied to the result of the command and will only include
strings that match the pattern specified. If the sorted set is large enough for scan commands to return
only a subset of the sorted set then there could be a case where the result is empty although there are
items that match the pattern specified. This is due to the default `COUNT` being `10` which indicates
that it will only fetch and match `10` items from the list.
*/
func (scanOptions *BaseScanOptions) SetMatch(m string) *BaseScanOptions {
scanOptions.match = m
return scanOptions
}

/*
`COUNT` is a just a hint for the command for how many elements to fetch from the
sorted set. `COUNT` could be ignored until the sorted set is large enough for the `SCAN` commands to
represent the results as compact single-allocation packed encoding.
*/
func (scanOptions *BaseScanOptions) SetCount(c int64) *BaseScanOptions {
scanOptions.count = c
return scanOptions
}

func (opts *BaseScanOptions) ToArgs() ([]string, error) {
args := []string{}
var err error
if opts.match != "" {
args = append(args, MatchKeyword, opts.match)
}

if opts.count != 0 {
args = append(args, CountKeyword, strconv.FormatInt(opts.count, 10))
}

return args, err
}
9 changes: 9 additions & 0 deletions go/api/options/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0

package options

const (
CountKeyword string = "COUNT" // Valkey API keyword used to extract specific number of matching indices from a list.
MatchKeyword string = "MATCH" // Valkey API keyword used to indicate the match filter.
NoValue string = "NOVALUE" // Valkey API keyword for the no value option for hcsan command.
)
43 changes: 43 additions & 0 deletions go/api/options/hscan_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0

package options

// This struct represents the optional arguments for the HSCAN command.
type HashScanOptions struct {
BaseScanOptions
noValue bool
}

func NewHashScanOptionsBuilder() *HashScanOptions {
return &HashScanOptions{}
}

/*
If this value is set to true, the HSCAN command will be called with NOVALUES option.
In the NOVALUES option, values are not included in the response.
*/
func (hashScanOptions *HashScanOptions) SetNoValue(noValue bool) *HashScanOptions {
hashScanOptions.noValue = noValue
return hashScanOptions
}

func (hashScanOptions *HashScanOptions) SetMatch(match string) *HashScanOptions {
hashScanOptions.BaseScanOptions.SetMatch(match)
return hashScanOptions
}

func (hashScanOptions *HashScanOptions) SetCount(count int64) *HashScanOptions {
hashScanOptions.BaseScanOptions.SetCount(count)
return hashScanOptions
}

func (options *HashScanOptions) ToArgs() ([]string, error) {
args := []string{}
baseArgs, err := options.BaseScanOptions.ToArgs()
args = append(args, baseArgs...)

if options.noValue {
args = append(args, NoValue)
}
return args, err
}
24 changes: 24 additions & 0 deletions go/api/response_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,30 @@ func handleStringSetResponse(response *C.struct_CommandResponse) (map[Result[str
return slice, nil
}

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

if response == nil || response.response_type == uint32(C.Null) {
return CreateNilKeyWithMemberAndScoreResult(), nil
}

typeErr := checkResponseType(response, C.Array, true)
if typeErr != nil {
return CreateNilKeyWithMemberAndScoreResult(), typeErr
}

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

arr := slice.([]interface{})
key := arr[0].(string)
member := arr[1].(string)
score := arr[2].(float64)
return CreateKeyWithMemberAndScoreResult(KeyWithMemberAndScore{key, member, score}), nil
}

func handleScanResponse(
response *C.struct_CommandResponse,
) (Result[string], []Result[string], error) {
Expand Down
16 changes: 16 additions & 0 deletions go/api/response_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ type Result[T any] struct {
isNil bool
}

// KeyWithMemberAndScore is used by BZPOPMIN/BZPOPMAX, which return an object consisting of the key of the sorted set that was
// popped, the popped member, and its score.
type KeyWithMemberAndScore struct {
Key string
Member string
Score float64
}

func (result Result[T]) IsNil() bool {
return result.isNil
}
Expand Down Expand Up @@ -47,6 +55,14 @@ func CreateNilBoolResult() Result[bool] {
return Result[bool]{val: false, isNil: true}
}

func CreateKeyWithMemberAndScoreResult(kmsVal KeyWithMemberAndScore) Result[KeyWithMemberAndScore] {
return Result[KeyWithMemberAndScore]{val: kmsVal, isNil: false}
}

func CreateNilKeyWithMemberAndScoreResult() Result[KeyWithMemberAndScore] {
return Result[KeyWithMemberAndScore]{val: KeyWithMemberAndScore{"", "", 0.0}, isNil: true}
}

// Enum to distinguish value types stored in `ClusterValue`
type ValueType int

Expand Down
Loading

0 comments on commit 809df46

Please sign in to comment.