Skip to content

Commit

Permalink
Go: Add command ZAdd (valkey-io#2813)
Browse files Browse the repository at this point in the history
* Go: Add command ZAdd

Signed-off-by: TJ Zhang <tj.zhang@improving.com>
  • Loading branch information
tjzhang-BQ authored Dec 20, 2024
1 parent b4231b0 commit 24a2dd0
Show file tree
Hide file tree
Showing 7 changed files with 398 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* Go: Add `SInterStore` ([#2779](https://github.com/valkey-io/valkey-glide/issues/2779))
* Node: Remove native package references for MacOs x64 architecture ([#2799](https://github.com/valkey-io/valkey-glide/issues/2799))
* Go: Add `SScan` and `SMove` ([#2789](https://github.com/valkey-io/valkey-glide/issues/2789))
* Go: Add `ZADD` ([#2813](https://github.com/valkey-io/valkey-glide/issues/2813))

#### Breaking Changes

Expand Down
90 changes: 88 additions & 2 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 All @@ -46,6 +48,7 @@ func successCallback(channelPtr unsafe.Pointer, cResponse *C.struct_CommandRespo
resultChannel <- payload{value: response, error: nil}
}

//
//export failureCallback
func failureCallback(channelPtr unsafe.Pointer, cErrorMessage *C.char, cErrorType C.RequestErrorType) {
resultChannel := *(*chan payload)(channelPtr)
Expand Down Expand Up @@ -102,7 +105,10 @@ func (client *baseClient) Close() {
client.coreClient = nil
}

func (client *baseClient) executeCommand(requestType C.RequestType, args []string) (*C.struct_CommandResponse, error) {
func (client *baseClient) executeCommand(
requestType C.RequestType,
args []string,
) (*C.struct_CommandResponse, error) {
if client.coreClient == nil {
return nil, &ClosingError{"ExecuteCommand failed. The client is closed."}
}
Expand Down Expand Up @@ -769,7 +775,10 @@ func (client *baseClient) LInsert(
return CreateNilInt64Result(), err
}

result, err := client.executeCommand(C.LInsert, []string{key, insertPositionStr, pivot, element})
result, err := client.executeCommand(
C.LInsert,
[]string{key, insertPositionStr, pivot, element},
)
if err != nil {
return CreateNilInt64Result(), err
}
Expand Down Expand Up @@ -1204,3 +1213,80 @@ func (client *baseClient) Renamenx(key string, newKey string) (Result[bool], err
}
return handleBooleanResponse(result)
}

func (client *baseClient) ZAdd(
key string,
membersScoreMap map[string]float64,
) (Result[int64], error) {
result, err := client.executeCommand(
C.ZAdd,
append([]string{key}, utils.ConvertMapToValueKeyStringArray(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, utils.ConvertMapToValueKeyStringArray(membersScoreMap)...),
)
if err != nil {
return CreateNilInt64Result(), err
}

return handleLongResponse(result)
}

func (client *baseClient) zAddIncrBase(key string, opts *options.ZAddOptions) (Result[float64], error) {
optionArgs, err := opts.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) ZAddIncr(
key string,
member string,
increment float64,
) (Result[float64], error) {
options, err := options.NewZAddOptionsBuilder().SetIncr(true, increment, member)
if err != nil {
return CreateNilFloat64Result(), err
}

return client.zAddIncrBase(key, options)
}

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
}

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

package options

import (
"errors"

"github.com/valkey-io/valkey-glide/go/glide/utils"
)

// Optional arguments to `ZAdd` in [SortedSetCommands]
type ZAddOptions struct {
conditionalChange ConditionalChange
updateOptions UpdateOptions
changed bool
incr bool
increment float64
member string
}

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

// `conditionalChange“ defines conditions for updating or adding elements with {@link SortedSetBaseCommands#zadd}
// command.
func (options *ZAddOptions) SetConditionalChange(c ConditionalChange) *ZAddOptions {
options.conditionalChange = c
return options
}

// `updateOptions` specifies conditions for updating scores with zadd command.
func (options *ZAddOptions) SetUpdateOptions(u UpdateOptions) *ZAddOptions {
options.updateOptions = u
return options
}

// `Changed` changes the return value from the number of new elements added to the total number of elements changed.
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
}

// `INCR` sets the increment value to use when incr is true.
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
}

// `ToArgs` converts the options to a list of arguments.
func (opts *ZAddOptions) ToArgs() ([]string, error) {
args := []string{}
var err error

if opts.conditionalChange == OnlyIfExists || opts.conditionalChange == OnlyIfDoesNotExist {
args = append(args, string(opts.conditionalChange))
}

if opts.updateOptions == ScoreGreaterThanCurrent || opts.updateOptions == ScoreLessThanCurrent {
args = append(args, string(opts.updateOptions))
}

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

if opts.incr {
args = append(args, IncrKeyword, utils.FloatToString(opts.increment), 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 "NX" 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
// "LT" in the Valkey API.
ScoreLessThanCurrent UpdateOptions = "LT"
// Only update existing elements if the new score is greater than the current score. Equivalent
// to "GT" 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.
)
91 changes: 91 additions & 0 deletions go/api/sorted_set_commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0

package api

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

// SortedSetCommands 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/#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:
// 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:
// 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:
// 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:
// 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 24a2dd0

Please sign in to comment.