Skip to content

Commit

Permalink
Go: Add command ZAdd
Browse files Browse the repository at this point in the history
Signed-off-by: TJ Zhang <tj.zhang@improving.com>
  • Loading branch information
TJ Zhang committed Dec 18, 2024
1 parent 3995396 commit e97a590
Show file tree
Hide file tree
Showing 6 changed files with 423 additions and 1 deletion.
72 changes: 72 additions & 0 deletions go/api/base_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"strconv"
"unsafe"

"github.com/valkey-io/valkey-glide/go/glide/api/options"
"github.com/valkey-io/valkey-glide/go/glide/protobuf"
"github.com/valkey-io/valkey-glide/go/glide/utils"
"google.golang.org/protobuf/proto"
Expand All @@ -26,6 +27,7 @@ type BaseClient interface {
HashCommands
ListCommands
SetCommands
SortedSetCommands
ConnectionManagementCommands
GenericBaseCommands
// Close terminates the client by closing all associated resources.
Expand Down Expand Up @@ -1111,3 +1113,73 @@ func (client *baseClient) PTTL(key string) (Result[int64], error) {

return handleLongResponse(result)
}

func (client *baseClient) Zadd(key string, membersScoreMap map[string]float64) (Result[int64], error) {
result, err := client.executeCommand(C.ZAdd, append([]string{key}, convertMapToKeyValueStringArray(membersScoreMap)...))
if err != nil {
return CreateNilInt64Result(), err
}

return handleLongResponse(result)
}

func (client *baseClient) ZaddWithOptions(
key string,
membersScoreMap map[string]float64,
opts *options.ZAddOptions,
) (Result[int64], error) {
optionArgs, err := opts.ToArgs()
if err != nil {
return CreateNilInt64Result(), err
}
commandArgs := append([]string{key}, optionArgs...)
result, err := client.executeCommand(C.ZAdd, append(commandArgs, convertMapToKeyValueStringArray(membersScoreMap)...))
if err != nil {
return CreateNilInt64Result(), err
}

return handleLongResponse(result)
}

func (client *baseClient) ZaddIncr(key string, member string, increment float64) (Result[float64], error) {
options, err := options.NewZaddOptionsBuilder().SetIncr(true, increment, member)
if err != nil {
return CreateNilFloat64Result(), err
}

optionArgs, err := options.ToArgs()
if err != nil {
return CreateNilFloat64Result(), err
}

result, err := client.executeCommand(C.ZAdd, append([]string{key}, optionArgs...))
if err != nil {
return CreateNilFloat64Result(), err
}

return handleDoubleResponse(result)
}

func (client *baseClient) ZaddIncrWithOptions(
key string,
member string,
increment float64,
opts *options.ZAddOptions,
) (Result[float64], error) {
incrOpts, err := opts.SetIncr(true, increment, member)
if err != nil {
return CreateNilFloat64Result(), err
}

optionArgs, err := incrOpts.ToArgs()
if err != nil {
return CreateNilFloat64Result(), err
}

result, err := client.executeCommand(C.ZAdd, append([]string{key}, optionArgs...))
if err != nil {
return CreateNilFloat64Result(), err
}

return handleDoubleResponse(result)
}
129 changes: 129 additions & 0 deletions go/api/options/zadd_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0

package options

import (
"errors"
"strconv"
)

/*
* Optional arguments to `Zadd` in [SortedSetCommands]
*/
type ZAddOptions struct {
/**
* Defines conditions for updating or adding elements with {@link SortedSetBaseCommands#zadd}
* command.
*/
conditionalChange ConditionalChange
/**
* Specifies conditions for updating scores with zadd {@link SortedSetBaseCommands#zadd} command.
*/
updateOptions UpdateOptions
/**
* Changes the return value from the number of new elements added to the total number of elements changed.
*/
changed bool
/**
* If true, the zadd command will act like ZINCRBY.
*/
incr bool
/**
* The increment value to use when incr is true.
*/
increment float64
/**
* The member to add to.
*/
member string
}

func NewZaddOptionsBuilder() *ZAddOptions {
return &ZAddOptions{}
}

func (options *ZAddOptions) SetConditionalChange(c ConditionalChange) *ZAddOptions {
options.conditionalChange = c
return options
}

func (options *ZAddOptions) SetUpdateOptions(u UpdateOptions) *ZAddOptions {
options.updateOptions = u
return options
}

func (options *ZAddOptions) SetChanged(ch bool) (*ZAddOptions, error) {
if options.incr {
return nil, errors.New("changed cannot be set when incr is true")
}
options.changed = ch
return options, nil
}

func (options *ZAddOptions) SetIncr(incr bool, increment float64, member string) (*ZAddOptions, error) {
if options.changed {
return nil, errors.New("incr cannot be set when changed is true")
}
options.incr = incr
options.increment = increment
options.member = member
return options, nil
}

func (opts *ZAddOptions) ToArgs() ([]string, error) {
args := []string{}
var err error

if opts.conditionalChange != "" {
args = append(args, string(opts.conditionalChange))
}

if opts.updateOptions != "" {
args = append(args, string(opts.updateOptions))
}

if opts.changed {
args = append(args, ChangedKeyword)
}

if opts.incr {
args = append(args, IncrKeyword, strconv.FormatFloat(opts.increment, 'f', -1, 64), opts.member)
}

return args, err
}

// A ConditionalSet defines whether a new value should be set or not.
type ConditionalChange string

const (
/**
* Only update elements that already exist. Don't add new elements. Equivalent to "XX" in the Valkey API.
*/
OnlyIfExists ConditionalChange = "XX"
/**
* Only add new elements. Don't update already existing elements. Equivalent to <code>NX</code> in
* the Valkey API.
*/
OnlyIfDoesNotExist ConditionalChange = "NX"
)

type UpdateOptions string

const (
/**
* Only update existing elements if the new score is less than the current score. Equivalent to
* <code>LT</code> in the Valkey API.
*/
ScoreLessThanCurrent UpdateOptions = "LT"
/**
* Only update existing elements if the new score is greater than the current score. Equivalent
* to <code>GT</code> in the Valkey API.
*/
ScoreGreaterThanCurrent UpdateOptions = "GT"
)

const (
ChangedKeyword string = "CH" // Valkey API keyword used to return total number of elements changed
IncrKeyword string = "INCR" // Valkey API keyword to make zadd act like ZINCRBY.
)
22 changes: 22 additions & 0 deletions go/api/response_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import "C"

import (
"fmt"
"strconv"
"unsafe"
)

Expand Down Expand Up @@ -372,3 +373,24 @@ func handleStringSetResponse(response *C.struct_CommandResponse) (map[Result[str

return slice, nil
}

// Function to convert a map[string, V] to a value-key string array, used in Zadd
func convertMapToKeyValueStringArray[V any](args map[string]V) []string {
result := make([]string, 0, len(args)*2)
for key, value := range args {
// Convert the value to a string after type checking
switch v := any(value).(type) {
case string:
result = append(result, v)
case int64:
result = append(result, strconv.FormatInt(v, 10))
case float64:
result = append(result, strconv.FormatFloat(v, 'f', -1, 64))
case bool:
result = append(result, strconv.FormatBool(v))
}
// Append the key
result = append(result, key)
}
return result
}
107 changes: 107 additions & 0 deletions go/api/sorted_set_commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0

package api

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

/** SetCommands supports commands and transactions for the "Sorted Set Commands" group for standalone and cluster clients.
*
* See [valkey.io] for details.
*
* [valkey.io]: https://valkey.io/commands/?group=sorted-set#sorted-set
*/
type SortedSetCommands interface {
/**
* Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.
*
* See [valkey.io] for details.
*
* Parameters:
* key - The key of the set.
* membersScoreMap - A map of members to their scores.
*
* Return value:
* Result[int64] - The number of members added to the set.
*
* Example:
* ```go
* res, err := client.Zadd(key, map[string]float64{"one": 1.0, "two": 2.0, "three": 3.0})
* fmt.Println(res.Value()) // Output: 3
* ```
*
* [valkey.io]: https://valkey.io/commands/zadd/
*/
Zadd(key string, membersScoreMap map[string]float64) (Result[int64], error)

/**
* Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.
*
* See [valkey.io] for details.
*
* Parameters:
* key - The key of the set.
* membersScoreMap - A map of members to their scores.
* opts - The options for the command. See [ZAddOptions] for details.
*
* Return value:
* Result[int64] - The number of members added to the set. If CHANGED is set, the number of members that were updated.
*
* Example:
* ```go
* res, err := client.ZaddWithOptions(key, map[string]float64{"one": 1.0, "two": 2.0, "three": 3.0}, options.NewZaddOptionsBuilder().SetChanged(true).Build())
* fmt.Println(res.Value()) // Output: 3
* ```
*
* [valkey.io]: https://valkey.io/commands/zadd/
*/
ZaddWithOptions(key string, membersScoreMap map[string]float64, opts *options.ZAddOptions) (Result[int64], error)

/**
* Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.
*
* See [valkey.io] for details.
*
* Parameters:
* key - The key of the set.
* member - The member to add to.
* increment - The increment to add to the member's score.
*
* Return value:
* Result[float64] - The new score of the member.
*
* Example:
* ```go
* res, err := client.ZaddIncr(key, "one", 1.0)
* fmt.Println(res.Value()) // Output: 1.0
* ```
*
* [valkey.io]: https://valkey.io/commands/zadd/
*/
ZaddIncr(key string, member string, increment float64) (Result[float64], error)

/**
* Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.
*
* See [valkey.io] for details.
*
* Parameters:
* key - The key of the set.
* member - The member to add to.
* increment - The increment to add to the member's score.
* opts - The options for the command. See [ZAddOptions] for details.
*
* Return value:
* Result[float64] - The new score of the member.
*
* Example:
* ```go
* res, err := client.ZaddIncrWithOptions(key, "one", 1.0, options.NewZaddOptionsBuilder().SetChanged(true))
* fmt.Println(res.Value()) // Output: 1.0
* ```
*
* [valkey.io]: https://valkey.io/commands/zadd/
*/
ZaddIncrWithOptions(key string, member string, increment float64, opts *options.ZAddOptions) (Result[float64], error)
}
2 changes: 1 addition & 1 deletion go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ module github.com/valkey-io/valkey-glide/go/glide
go 1.20

require (
github.com/google/uuid v1.6.0
github.com/stretchr/testify v1.8.4
google.golang.org/protobuf v1.33.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
Expand Down
Loading

0 comments on commit e97a590

Please sign in to comment.