Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
Sa1Gur authored Jan 31, 2025
2 parents f43621b + 324f9e4 commit 555d1cd
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 2 deletions.
86 changes: 84 additions & 2 deletions go/api/base_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5877,6 +5877,88 @@ func (client *baseClient) ZRemRangeByScore(key string, rangeQuery options.RangeB
return handleIntResponse(result)
}

// Returns a random member from the sorted set stored at `key`.
//
// See [valkey.io] for details.
//
// Parameters:
//
// key - The key of the sorted set.
//
// Return value:
//
// A string representing a random member from the sorted set.
// If the sorted set does not exist or is empty, the response will be `nil`.
//
// Example:
//
// member, err := client.ZRandMember("key1")
//
// [valkey.io]: https://valkey.io/commands/zrandmember/
func (client *baseClient) ZRandMember(key string) (Result[string], error) {
result, err := client.executeCommand(C.ZRandMember, []string{key})
if err != nil {
return CreateNilStringResult(), err
}
return handleStringOrNilResponse(result)
}

// Returns a random member from the sorted set stored at `key`.
//
// See [valkey.io] for details.
//
// Parameters:
//
// key - The key of the sorted set.
// count - The number of field names to return.
// If `count` is positive, returns unique elements. If negative, allows for duplicates.
//
// Return value:
//
// An array of members from the sorted set.
// If the sorted set does not exist or is empty, the response will be an empty array.
//
// Example:
//
// members, err := client.ZRandMemberWithCount("key1", -5)
//
// [valkey.io]: https://valkey.io/commands/zrandmember/
func (client *baseClient) ZRandMemberWithCount(key string, count int64) ([]string, error) {
result, err := client.executeCommand(C.ZRandMember, []string{key, utils.IntToString(count)})
if err != nil {
return nil, err
}
return handleStringArrayResponse(result)
}

// Returns a random member from the sorted set stored at `key`.
//
// See [valkey.io] for details.
//
// Parameters:
//
// key - The key of the sorted set.
// count - The number of field names to return.
// If `count` is positive, returns unique elements. If negative, allows for duplicates.
//
// Return value:
//
// An array of `MemberAndScore` objects, which store member names and their respective scores.
// If the sorted set does not exist or is empty, the response will be an empty array.
//
// Example:
//
// membersAndScores, err := client.ZRandMemberWithCountWithScores("key1", 5)
//
// [valkey.io]: https://valkey.io/commands/zrandmember/
func (client *baseClient) ZRandMemberWithCountWithScores(key string, count int64) ([]MemberAndScore, error) {
result, err := client.executeCommand(C.ZRandMember, []string{key, utils.IntToString(count), options.WithScores})
if err != nil {
return nil, err
}
return handleMemberAndScoreArrayResponse(result)
}

// Returns the scores associated with the specified `members` in the sorted set stored at `key`.
//
// Since:
Expand All @@ -5890,8 +5972,8 @@ func (client *baseClient) ZRemRangeByScore(key string, rangeQuery options.RangeB
//
// 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`.
// 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:
//
Expand Down
1 change: 1 addition & 0 deletions go/api/options/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const (
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.
WithScore string = "WITHSCORE" // Valkey API keyword for the with score option for zrank and zrevrank commands.
WithScores string = "WITHSCORES" // Valkey API keyword for ZRandMember command to return scores along with members.
NoScores string = "NOSCORES" // Valkey API keyword for the no scores option for zscan command.
WithValues string = "WITHVALUES" // Valkey API keyword to query hash values along their names in `HRANDFIELD`.
)
Expand Down
21 changes: 21 additions & 0 deletions go/api/response_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,27 @@ func handleKeyWithArrayOfMembersAndScoresResponse(
return CreateKeyWithArrayOfMembersAndScoresResult(KeyWithArrayOfMembersAndScores{key, memberAndScoreArray}), nil
}

func handleMemberAndScoreArrayResponse(response *C.struct_CommandResponse) ([]MemberAndScore, error) {
defer C.free_command_response(response)

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

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

var result []MemberAndScore
for _, arr := range slice.([]interface{}) {
pair := arr.([]interface{})
result = append(result, MemberAndScore{pair[0].(string), pair[1].(float64)})
}
return result, nil
}

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

Expand Down
1 change: 1 addition & 0 deletions go/api/response_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type KeyWithArrayOfMembersAndScores struct {
MembersAndScores []MemberAndScore
}

// MemberAndScore is used by ZRANDMEMBER, which return an object consisting of the sorted set member, and its score.
type MemberAndScore struct {
Member string
Score float64
Expand Down
6 changes: 6 additions & 0 deletions go/api/sorted_set_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,11 @@ type SortedSetCommands interface {

ZRemRangeByScore(key string, rangeQuery options.RangeByScore) (int64, error)

ZRandMember(key string) (Result[string], error)

ZRandMemberWithCount(key string, count int64) ([]string, error)

ZRandMemberWithCountWithScores(key string, count int64) ([]MemberAndScore, error)

ZMScore(key string, members []string) ([]Result[float64], error)
}
62 changes: 62 additions & 0 deletions go/integTest/shared_commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6496,6 +6496,68 @@ func (suite *GlideTestSuite) TestZMScore() {
})
}

func (suite *GlideTestSuite) TestZRandMember() {
suite.runWithDefaultClients(func(client api.BaseClient) {
t := suite.T()
key1 := uuid.NewString()
key2 := uuid.NewString()
members := []string{"one", "two"}

zadd, err := client.ZAdd(key1, map[string]float64{"one": 1.0, "two": 2.0})
assert.NoError(t, err)
assert.Equal(t, int64(2), zadd)

randomMember, err := client.ZRandMember(key1)
assert.NoError(t, err)
assert.Contains(t, members, randomMember.Value())

// unique values are expected as count is positive
randomMembers, err := client.ZRandMemberWithCount(key1, 4)
assert.NoError(t, err)
assert.ElementsMatch(t, members, randomMembers)

membersAndScores, err := client.ZRandMemberWithCountWithScores(key1, 4)
expectedMembersAndScores := []api.MemberAndScore{{Member: "one", Score: 1}, {Member: "two", Score: 2}}
assert.NoError(t, err)
assert.ElementsMatch(t, expectedMembersAndScores, membersAndScores)

// Duplicate values are expected as count is negative
randomMembers, err = client.ZRandMemberWithCount(key1, -4)
assert.NoError(t, err)
assert.Len(t, randomMembers, 4)
for _, member := range randomMembers {
assert.Contains(t, members, member)
}

membersAndScores, err = client.ZRandMemberWithCountWithScores(key1, -4)
assert.NoError(t, err)
assert.Len(t, membersAndScores, 4)
for _, memberAndScore := range membersAndScores {
assert.Contains(t, expectedMembersAndScores, memberAndScore)
}

// non existing key should return null or empty array
randomMember, err = client.ZRandMember(key2)
assert.NoError(t, err)
assert.True(t, randomMember.IsNil())
randomMembers, err = client.ZRandMemberWithCount(key2, -4)
assert.NoError(t, err)
assert.Len(t, randomMembers, 0)
membersAndScores, err = client.ZRandMemberWithCountWithScores(key2, -4)
assert.NoError(t, err)
assert.Len(t, membersAndScores, 0)

// Key exists, but is not a set
suite.verifyOK(client.Set(key2, "ZRandMember"))
_, err = client.ZRandMember(key2)
assert.IsType(suite.T(), &errors.RequestError{}, err)
_, err = client.ZRandMemberWithCount(key2, 2)
assert.IsType(suite.T(), &errors.RequestError{}, err)
_, err = client.ZRandMemberWithCountWithScores(key2, 2)
assert.IsType(suite.T(), &errors.RequestError{}, err)
})
}

func (suite *GlideTestSuite) TestObjectIdleTime() {
suite.runWithDefaultClients(func(client api.BaseClient) {
defaultClient := suite.defaultClient()
Expand Down

0 comments on commit 555d1cd

Please sign in to comment.