Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go: Add Zcount command #2930

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#### Changes

* Go: Add Zcount command ([#2930](https://github.com/valkey-io/valkey-glide/pull/2930))
* 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))
Expand Down
12 changes: 12 additions & 0 deletions go/api/base_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1483,3 +1483,15 @@ func (client *baseClient) Persist(key string) (Result[bool], error) {
}
return handleBooleanResponse(result)
}

func (client *baseClient) ZCount(key string, rangeOptions *options.ZCountRange) (Result[int64], error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func (client *baseClient) ZCount(key string, rangeOptions *options.ZCountRange) (Result[int64], error) {
func (client *baseClient) ZCount(key string, rangeOptions *options.ZCountRange) (int64, error) {

return value is not nullable

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update tests accordingly

zCountRangeArgs, err := rangeOptions.ToArgs()
if err != nil {
return CreateNilInt64Result(), err
}
result, err := client.executeCommand(C.ZCount, append([]string{key}, zCountRangeArgs...))
if err != nil {
return CreateNilInt64Result(), err
}
return handleLongResponse(result)
}
114 changes: 114 additions & 0 deletions go/api/options/zcount_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0

package options

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

// The common interface for representing all the range type for Zcount command.
type ScoreRange interface {
ToArgs() ([]string, error)
}

type (
InfBoundary string
)

const (
// The highest bound in the sorted set
PositiveInfinity InfBoundary = "+inf"
// The lowest bound in the sorted set
NegativeInfinity InfBoundary = "-inf"
)

// This struct represents the infinity boundary for a score range.
type InfScoreBound struct {
value InfBoundary
}

// Create a new infinite score boundary
func NewInfScoreBoundBuilder() *InfScoreBound {
return &InfScoreBound{}
}

// The value of the infinite score bound.
func (infScoreBound *InfScoreBound) SetValue(value InfBoundary) *InfScoreBound {
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
infScoreBound.value = value
return infScoreBound
}

func (infScoreBound *InfScoreBound) ToArgs() ([]string, error) {
args := []string{}
args = append(args, string(infScoreBound.value))
return args, nil
prateek-kumar-improving marked this conversation as resolved.
Show resolved Hide resolved
}

// This struct represents score boundary for a bound.
type ScoreBoundary struct {
bound float64
isInclusive bool
}

// Create a new score boundary.
func NewScoreBoundaryBuilder() *ScoreBoundary {
return &ScoreBoundary{isInclusive: true}
}

// Set the bound for a score boundary.
func (scoreBoundary *ScoreBoundary) SetBound(bound float64) *ScoreBoundary {
scoreBoundary.bound = bound
return scoreBoundary
}

// Set if the bound for a score boundary is inclusive or not inclusive in the boundary.
func (scoreBoundary *ScoreBoundary) SetIsInclusive(isInclusive bool) *ScoreBoundary {
scoreBoundary.isInclusive = isInclusive
return scoreBoundary
}

func (scoreBoundary *ScoreBoundary) ToArgs() ([]string, error) {
args := []string{}
if !scoreBoundary.isInclusive {
args = append(args, "("+utils.FloatToString(scoreBoundary.bound))
} else {
args = append(args, utils.FloatToString(scoreBoundary.bound))
}
return args, nil
}

// This struct represents the min and max boundary for the Zcount command.
type ZCountRange struct {
min ScoreRange
max ScoreRange
}

// Create a new Zcount range.
func NewZCountRangeBuilder() *ZCountRange {
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
return &ZCountRange{}
}

// Set the minimum value for the Zcount command range.
func (zCountRange *ZCountRange) SetMin(min ScoreRange) *ZCountRange {
zCountRange.min = min
return zCountRange
}

// Set the maximum value for the Zcount command range.
func (zCountRange *ZCountRange) SetMax(max ScoreRange) *ZCountRange {
zCountRange.max = max
return zCountRange
}

func (zCountRange *ZCountRange) ToArgs() ([]string, error) {
args := []string{}
minArgs, err := zCountRange.min.ToArgs()
if err != nil {
return nil, err
}
args = append(args, minArgs...)
maxArgs, err := zCountRange.max.ToArgs()
if err != nil {
return nil, err
}
args = append(args, maxArgs...)
return args, nil
}
29 changes: 29 additions & 0 deletions go/api/sorted_set_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,4 +262,33 @@ type SortedSetCommands interface {
// [valkey.io]: https://valkey.io/commands/bzpopmin/
// [blocking commands]: https://github.com/valkey-io/valkey-glide/wiki/General-Concepts#blocking-commands
BZPopMin(keys []string, timeoutSecs float64) (Result[KeyWithMemberAndScore], error)

// Returns the number of members in the sorted set stored at `key` with scores between `min` and `max` score.
//
// See [valkey.io] for details.
//
// Parameters:
// key - The key of the set.
// rangeOptions - Contains `min` and `max` score. `min` contains the minimum score to count from.
// `max` contains the maximum score to count up to. Can be positive/negative infinity, or
// specific score and inclusivity.
//
// Return value:
// Result[int64] - The number of members in the specified score range.
prateek-kumar-improving marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Result[int64] - The number of members in the specified score range.
// The number of members in the specified score range.

//
// Example:
// key1 := uuid.NewString()
// membersScores := map[string]float64{"one": 1.0, "two": 2.0, "three": 3.0 }
// zAddResult, err := client.ZAdd(key1, membersScores)
// zCountRange := options.NewZCountRangeBuilder()
// zCountRange.SetMin(options.NewInfScoreBoundBuilder().SetValue(options.NegativeInfinity))
// zCountRange.SetMax(options.NewInfScoreBoundBuilder().SetValue(options.PositiveInfinity))
prateek-kumar-improving marked this conversation as resolved.
Show resolved Hide resolved
// zCountResult, err := client.ZCount(key1, zCountRange)
// if err!= nil {
// // Print err
// }
// fmt.Println(zCountResult.Value()) // Output: 3
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// fmt.Println(zCountResult.Value()) // Output: 3
// fmt.Println(zCountResult) // Output: 3

//
// [valkey.io]: https://valkey.io/commands/zcount/
ZCount(key string, rangeOptions *options.ZCountRange) (Result[int64], error)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ZCount(key string, rangeOptions *options.ZCountRange) (Result[int64], error)
ZCount(key string, rangeOptions *options.ZCountRange) (int64, error)

}
72 changes: 72 additions & 0 deletions go/integTest/shared_commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4484,3 +4484,75 @@ func (suite *GlideTestSuite) TestPersist() {
assert.False(t, resultInvalidKey.Value())
})
}

func (suite *GlideTestSuite) TestZCount() {
suite.runWithDefaultClients(func(client api.BaseClient) {
key1 := uuid.NewString()
key2 := uuid.NewString()
membersScores := map[string]float64{
"one": 1.0,
"two": 2.0,
"three": 3.0,
}
t := suite.T()
res1, err := client.ZAdd(key1, membersScores)
assert.Nil(t, err)
assert.Equal(t, int64(3), res1.Value())

// In range negative to positive infinity.
zCountRange := options.NewZCountRangeBuilder()
zCountRange.SetMin(options.NewInfScoreBoundBuilder().SetValue(options.NegativeInfinity))
zCountRange.SetMax(options.NewInfScoreBoundBuilder().SetValue(options.PositiveInfinity))
zCountResult, err := client.ZCount(key1, zCountRange)
assert.Nil(t, err)
assert.Equal(t, int64(3), zCountResult.Value())
zCountRange = options.NewZCountRangeBuilder()
zCountRange.SetMin(options.NewScoreBoundaryBuilder().SetBound(math.Inf(-1)))
zCountRange.SetMax(options.NewScoreBoundaryBuilder().SetBound(math.Inf(+1)))
zCountResult, err = client.ZCount(key1, zCountRange)
assert.Nil(t, err)
assert.Equal(t, int64(3), zCountResult.Value())

// In range 1 (exclusive) to 3 (inclusive)
zCountRange = options.NewZCountRangeBuilder()
zCountRange.SetMin(options.NewScoreBoundaryBuilder().SetBound(1).SetIsInclusive(false))
zCountRange.SetMax(options.NewScoreBoundaryBuilder().SetBound(3).SetIsInclusive(true))
zCountResult, err = client.ZCount(key1, zCountRange)
assert.Nil(t, err)
assert.Equal(t, int64(2), zCountResult.Value())

// In range negative infinity to 3 (inclusive)
zCountRange = options.NewZCountRangeBuilder()
zCountRange.SetMin(options.NewInfScoreBoundBuilder().SetValue(options.NegativeInfinity))
zCountRange.SetMax(options.NewScoreBoundaryBuilder().SetBound(3).SetIsInclusive(true))
zCountResult, err = client.ZCount(key1, zCountRange)
assert.Nil(t, err)
assert.Equal(t, int64(3), zCountResult.Value())

// Incorrect range start > end
zCountRange = options.NewZCountRangeBuilder()
zCountRange.SetMin(options.NewInfScoreBoundBuilder().SetValue(options.PositiveInfinity))
zCountRange.SetMax(options.NewScoreBoundaryBuilder().SetBound(3).SetIsInclusive(true))
zCountResult, err = client.ZCount(key1, zCountRange)
assert.Nil(t, err)
assert.Equal(t, int64(0), zCountResult.Value())

// Non-existing key
zCountRange = options.NewZCountRangeBuilder()
zCountRange.SetMin(options.NewInfScoreBoundBuilder().SetValue(options.NegativeInfinity))
zCountRange.SetMax(options.NewInfScoreBoundBuilder().SetValue(options.PositiveInfinity))
zCountResult, err = client.ZCount("non_existing_key", zCountRange)
assert.Nil(t, err)
assert.Equal(t, int64(0), zCountResult.Value())

// Key exists, but it is not a set
setResult, _ := client.Set(key2, "value")
assert.Equal(t, setResult.Value(), "OK")
zCountRange = options.NewZCountRangeBuilder()
zCountRange.SetMin(options.NewInfScoreBoundBuilder().SetValue(options.NegativeInfinity))
zCountRange.SetMax(options.NewInfScoreBoundBuilder().SetValue(options.PositiveInfinity))
_, err = client.ZCount(key2, zCountRange)
assert.NotNil(t, err)
assert.IsType(suite.T(), &api.RequestError{}, err)
})
}
Loading