From a852c443968bf328fc21cd3885cfe9509bd806d1 Mon Sep 17 00:00:00 2001 From: Yury-Fridlyand Date: Sat, 25 Jan 2025 10:24:21 -0800 Subject: [PATCH] Go: `ZMSCORE`. Signed-off-by: Yury-Fridlyand --- go/api/base_client.go | 30 +++++++++++++++++ go/api/response_handlers.go | 27 +++++++++++++++ go/api/sorted_set_commands.go | 2 ++ go/integTest/shared_commands_test.go | 50 ++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+) diff --git a/go/api/base_client.go b/go/api/base_client.go index 9bb0cbb85f..b9618af437 100644 --- a/go/api/base_client.go +++ b/go/api/base_client.go @@ -5811,6 +5811,36 @@ func (client *baseClient) ZRemRangeByScore(key string, rangeQuery options.RangeB return handleIntResponse(result) } +// Returns the scores associated with the specified `members` in the sorted set stored at `key`. +// +// Since: +// +// Valkey 6.2.0 and above. +// +// Parameters: +// +// key - The key of the sorted set. +// members - A list of members in the sorted set. +// +// Return value: +// +// An array of scores corresponding to `members`. +// If a member does not exist in the sorted set, the corresponding value in the list will be `nil`. +// +// Example: +// +// result, err := client.ZMScore(key, []string{"member1", "non_existent_member", "member2"}) +// result: [{1.0 false} {0 true} {2.0 false}] +// +// [valkey.io]: https://valkey.io/commands/zmscore/ +func (client *baseClient) ZMScore(key string, members []string) ([]Result[float64], error) { + response, err := client.executeCommand(C.ZMScore, append([]string{key}, members...)) + if err != nil { + return nil, err + } + return handleFloatOrNilArrayResponse(response) +} + // Returns the logarithmic access frequency counter of a Valkey object stored at key. // // Parameters: diff --git a/go/api/response_handlers.go b/go/api/response_handlers.go index 6e84817123..afe3633f2b 100644 --- a/go/api/response_handlers.go +++ b/go/api/response_handlers.go @@ -347,6 +347,33 @@ func handleFloatOrNilResponse(response *C.struct_CommandResponse) (Result[float6 return CreateFloat64Result(float64(response.float_value)), nil } +// elements in the array could be `null`, but array isn't +func handleFloatOrNilArrayResponse(response *C.struct_CommandResponse) ([]Result[float64], error) { + defer C.free_command_response(response) + + typeErr := checkResponseType(response, C.Array, true) + if typeErr != nil { + return nil, typeErr + } + + slice := make([]Result[float64], 0, response.array_value_len) + for _, v := range unsafe.Slice(response.array_value, response.array_value_len) { + if v.response_type == C.Null { + slice = append(slice, CreateNilFloat64Result()) + continue + } + + err := checkResponseType(&v, C.Float, false) + if err != nil { + return nil, err + } + + slice = append(slice, CreateFloat64Result(float64(v.float_value))) + } + + return slice, nil +} + func handleLongAndDoubleOrNullResponse(response *C.struct_CommandResponse) (Result[int64], Result[float64], error) { defer C.free_command_response(response) diff --git a/go/api/sorted_set_commands.go b/go/api/sorted_set_commands.go index 1aecdfeb1d..5f9e3902cc 100644 --- a/go/api/sorted_set_commands.go +++ b/go/api/sorted_set_commands.go @@ -61,4 +61,6 @@ type SortedSetCommands interface { ZRemRangeByRank(key string, start int64, stop int64) (int64, error) ZRemRangeByScore(key string, rangeQuery options.RangeByScore) (int64, error) + + ZMScore(key string, members []string) ([]Result[float64], error) } diff --git a/go/integTest/shared_commands_test.go b/go/integTest/shared_commands_test.go index ce31235414..f492face91 100644 --- a/go/integTest/shared_commands_test.go +++ b/go/integTest/shared_commands_test.go @@ -6393,6 +6393,56 @@ func (suite *GlideTestSuite) TestZRemRangeByScore() { }) } +func (suite *GlideTestSuite) TestZMScore() { + suite.SkipIfServerVersionLowerThanBy("6.2.0") + suite.runWithDefaultClients(func(client api.BaseClient) { + key := uuid.NewString() + + zAddResult, err := client.ZAdd(key, map[string]float64{"one": 1.0, "two": 2.0, "three": 3.0}) + assert.NoError(suite.T(), err) + assert.Equal(suite.T(), int64(3), zAddResult) + + res, err := client.ZMScore(key, []string{"one", "three", "two"}) + expected := []api.Result[float64]{ + api.CreateFloat64Result(1), + api.CreateFloat64Result(3), + api.CreateFloat64Result(2), + } + assert.NoError(suite.T(), err) + assert.Equal(suite.T(), expected, res) + + // not existing members + res, err = client.ZMScore(key, []string{"nonExistingMember", "two", "nonExistingMember"}) + expected = []api.Result[float64]{ + api.CreateNilFloat64Result(), + api.CreateFloat64Result(2), + api.CreateNilFloat64Result(), + } + assert.NoError(suite.T(), err) + assert.Equal(suite.T(), expected, res) + + // not existing key + res, err = client.ZMScore(uuid.NewString(), []string{"one", "three", "two"}) + expected = []api.Result[float64]{ + api.CreateNilFloat64Result(), + api.CreateNilFloat64Result(), + api.CreateNilFloat64Result(), + } + assert.NoError(suite.T(), err) + assert.Equal(suite.T(), expected, res) + + // invalid arg - member list must not be empty + _, err = client.ZMScore(key, []string{}) + assert.IsType(suite.T(), &errors.RequestError{}, err) + + // key exists, but it is not a sorted set + key2 := uuid.NewString() + suite.verifyOK(client.Set(key2, "ZMScore")) + _, err = client.ZMScore(key2, []string{"one"}) + assert.IsType(suite.T(), &errors.RequestError{}, err) + }) +} + func (suite *GlideTestSuite) TestObjectIdleTime() { suite.runWithDefaultClients(func(client api.BaseClient) { defaultClient := suite.defaultClient()