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: Implement Sort, Sort ReadOnly and Sort Store commands #2888

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
51 changes: 51 additions & 0 deletions go/api/base_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1441,3 +1441,54 @@ func (client *baseClient) ZCard(key string) (Result[int64], error) {

return handleLongResponse(result)
}

func (client *baseClient) Sort(key string) ([]Result[string], error) {
result, err := client.executeCommand(C.Sort, []string{key})
if err != nil {
return nil, err
}
return handleStringArrayResponse(result)
}

func (client *baseClient) SortWithOptions(key string, options *options.SortOptions) ([]Result[string], error) {
optionArgs := options.ToArgs()
result, err := client.executeCommand(C.Sort, append([]string{key}, optionArgs...))
if err != nil {
return nil, err
}
return handleStringArrayResponse(result)
}

func (client *baseClient) SortReadOnly(key string) ([]Result[string], error) {
result, err := client.executeCommand(C.SortReadOnly, []string{key})
if err != nil {
return nil, err
}
return handleStringArrayResponse(result)
}

func (client *baseClient) SortReadOnlyWithOptions(key string, options *options.SortOptions) ([]Result[string], error) {
optionArgs := options.ToArgs()
result, err := client.executeCommand(C.SortReadOnly, append([]string{key}, optionArgs...))
if err != nil {
return nil, err
}
return handleStringArrayResponse(result)
}

func (client *baseClient) SortStore(key string, destination string) (Result[int64], error) {
result, err := client.executeCommand(C.Sort, []string{key, "STORE", destination})
if err != nil {
return CreateNilInt64Result(), err
}
return handleLongResponse(result)
}

func (client *baseClient) SortStoreWithOptions(key string, destination string, options *options.SortOptions) (Result[int64], error) {
optionArgs := options.ToArgs()
result, err := client.executeCommand(C.Sort, append([]string{key, "STORE", destination}, optionArgs...))
if err != nil {
return CreateNilInt64Result(), err
}
return handleLongResponse(result)
}
176 changes: 174 additions & 2 deletions go/api/generic_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

package api

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

// Supports commands and transactions for the "Generic" group of commands for standalone and cluster clients.
//
// See [valkey.io] for details.
Expand All @@ -11,7 +13,7 @@ type GenericBaseCommands interface {
// Del removes the specified keys from the database. A key is ignored if it does not exist.
//
// Note:
// In cluster mode, if keys in `keyValueMap` map to different hash slots, the command
// In cluster mode, if `key` and `destination` map to different hash slots, the command
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
// In cluster mode, if `key` and `destination` map to different hash slots, the command
// In cluster mode, if `keys` map to different hash slots, the command

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

// will be split across these slots and executed separately for each. This means the command
// is atomic only at the slot level. If one or more slot-specific requests fail, the entire
// call will return the first encountered error, even though some requests may have succeeded
Expand All @@ -37,7 +39,7 @@ type GenericBaseCommands interface {
// Exists returns the number of keys that exist in the database
//
// Note:
// In cluster mode, if keys in `keyValueMap` map to different hash slots, the command
// In cluster mode, if `key` and `destination` map to different hash slots, the command
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
// In cluster mode, if `key` and `destination` map to different hash slots, the command
// In cluster mode, if `keys` map to different hash slots, the command

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

// will be split across these slots and executed separately for each. This means the command
// is atomic only at the slot level. If one or more slot-specific requests fail, the entire
// call will return the first encountered error, even though some requests may have succeeded
Expand Down Expand Up @@ -312,6 +314,176 @@ type GenericBaseCommands interface {
// [valkey.io]: https://valkey.io/commands/pttl/
PTTL(key string) (Result[int64], error)

// Sorts the elements in the list, set, or sorted set at key and returns the result.
// The sort command can be used to sort elements based on different criteria and apply
// transformations on sorted elements.
// To store the result into a new key, see {@link #sortStore(string, string)}.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Fix link formats (apply throughout)

//
// Parameters:
// key - The key of the list, set, or sorted set to be sorted.
//
// Return value:
// An Array of sorted elements.
//
// Example:
//
// result, err := client.Sort("key")
// result.Value(): [{1 false} {2 false} {3 false}]
// result.IsNil(): false
//
// [valkey.io]: https://valkey.io/commands/sort/
Sort(key string) ([]Result[string], error)
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved

// Sorts the elements in the list, set, or sorted set at key and returns the result.
// The sort command can be used to sort elements based on different criteria and apply
// transformations on sorted elements.
// To store the result into a new key, see {@link #sortStore(string, string)}.
//
// Note:
// In cluster mode, if `key` and `destination` map to different hash slots, the command
// will be split across these slots and executed separately for each. This means the command
// is atomic only at the slot level. If one or more slot-specific requests fail, the entire
// call will return the first encountered error, even though some requests may have succeeded
// while others did not. If this behavior impacts your application logic, consider splitting
// the request into sub-requests per slot to ensure atomicity.
// The use of {@link SortOptions#byPattern} and {@link SortOptions#getPatterns} in cluster mode is
// supported since Valkey version 8.0.
//
// Parameters:
// key - The key of the list, set, or sorted set to be sorted.
// sortOptions- The {@link SortOptions}.
//
// Return value:
// An Array of sorted elements.
//
// Example:
//
// options := api.NewSortOptions().SetByPattern("weight_*").SetIsAlpha(false).AddGetPattern("object_*").AddGetPattern("#")
// result, err := client.Sort("key", options)
// result.Value(): [{Object_3 false} {c false} {Object_1 false} {a false} {Object_2 false} {b false}]
// result.IsNil(): false
//
// [valkey.io]: https://valkey.io/commands/sort/
SortWithOptions(key string, sortOptions *options.SortOptions) ([]Result[string], error)

// Sorts the elements in the list, set, or sorted set at key and stores the result in
// destination. The sort command can be used to sort elements based on
// different criteria, apply transformations on sorted elements, and store the result in a new key.
// The sort command can be used to sort elements based on different criteria and apply
// transformations on sorted elements.
// To get the sort result without storing it into a key, see {@link #sort(String)} or {@link #sortReadOnly(String)}.
//
// Note:
// In cluster mode, if `key` and `destination` map to different hash slots, the command
// will be split across these slots and executed separately for each. This means the command
// is atomic only at the slot level. If one or more slot-specific requests fail, the entire
// call will return the first encountered error, even though some requests may have succeeded
// while others did not. If this behavior impacts your application logic, consider splitting
// the request into sub-requests per slot to ensure atomicity.
//
// Parameters:
// key - The key of the list, set, or sorted set to be sorted.
// destination - The key where the sorted result will be stored.
//
// Return value:
// The number of elements in the sorted key stored at destination.
//
// Example:
//
// result, err := client.SortStore("key","destkey")
// result.Value(): 1
// result.IsNil(): false
//
// [valkey.io]: https://valkey.io/commands/sort/
SortStore(key string, destination string) (Result[int64], error)

// Sorts the elements in the list, set, or sorted set at key and stores the result in
// destination. The sort command can be used to sort elements based on
// different criteria, apply transformations on sorted elements, and store the result in a new key.
// The sort command can be used to sort elements based on different criteria and apply
// transformations on sorted elements.
// To get the sort result without storing it into a key, see {@link #sort(String)} or {@link #sortReadOnly(String)}.
//
// Note:
// In cluster mode, if `key` and `destination` map to different hash slots, the command
// will be split across these slots and executed separately for each. This means the command
// is atomic only at the slot level. If one or more slot-specific requests fail, the entire
// call will return the first encountered error, even though some requests may have succeeded
// while others did not. If this behavior impacts your application logic, consider splitting
// the request into sub-requests per slot to ensure atomicity.
// The use of {@link SortOptions#byPattern} and {@link SortOptions#getPatterns}
// in cluster mode is supported since Valkey version 8.0.
//
// Parameters:
// key - The key of the list, set, or sorted set to be sorted.
// destination - The key where the sorted result will be stored.
// sortOptions- The {@link SortOptions}.
//
// Return value:
// The number of elements in the sorted key stored at destination.
//
// Example:
//
// options := api.NewSortOptions().SetByPattern("weight_*").SetIsAlpha(false).AddGetPattern("object_*").AddGetPattern("#")
// result, err := client.SortStore("key","destkey",options)
// result.Value(): 1
// result.IsNil(): false
//
// [valkey.io]: https://valkey.io/commands/sort/
SortStoreWithOptions(key string, destination string, sortOptions *options.SortOptions) (Result[int64], error)

// Sorts the elements in the list, set, or sorted set at key and returns the result.
// The sortReadOnly command can be used to sort elements based on different criteria and apply
// transformations on sorted elements.
// This command is routed depending on the client's {@link ReadFrom} strategy.
//
// Parameters:
// key - The key of the list, set, or sorted set to be sorted.
//
// Return value:
// An Array of sorted elements.
//
// Example:
//
// result, err := client.SortReadOnly("key")
// result.Value(): [{1 false} {2 false} {3 false}]
// result.IsNil(): false
//
// [valkey.io]: https://valkey.io/commands/sort/
SortReadOnly(key string) ([]Result[string], error)

// Sorts the elements in the list, set, or sorted set at key and returns the result.
// The sort command can be used to sort elements based on different criteria and apply
// transformations on sorted elements.
// This command is routed depending on the client's {@link ReadFrom} strategy.
//
// Note:
// In cluster mode, if `key` and `destination` map to different hash slots, the command
Copy link
Collaborator

Choose a reason for hiding this comment

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

there is no destination, but key names could be referenced in options

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

// will be split across these slots and executed separately for each. This means the command
// is atomic only at the slot level. If one or more slot-specific requests fail, the entire
// call will return the first encountered error, even though some requests may have succeeded
// while others did not. If this behavior impacts your application logic, consider splitting
// the request into sub-requests per slot to ensure atomicity.
// The use of {@link SortOptions#byPattern} and {@link SortOptions#getPatterns} in cluster mode is
// supported since Valkey version 8.0.
//
// Parameters:
// key - The key of the list, set, or sorted set to be sorted.
// sortOptions- The {@link SortOptions}.
//
// Return value:
// An Array of sorted elements.
//
// Example:
//
// options := api.NewSortOptions().SetByPattern("weight_*").SetIsAlpha(false).AddGetPattern("object_*").AddGetPattern("#")
// result, err := client.SortReadOnly("key", options)
// result.Value(): [{Object_3 false} {c false} {Object_1 false} {a false} {Object_2 false} {b false}]
// result.IsNil(): false
//
// [valkey.io]: https://valkey.io/commands/sort/
SortReadOnlyWithOptions(key string, sortOptions *options.SortOptions) ([]Result[string], error)

// Unlink (delete) multiple keys from the database. A key is ignored if it does not exist.
// This command, similar to Del However, this command does not block the server
//
Expand Down
129 changes: 129 additions & 0 deletions go/api/options/sort_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 (
"github.com/valkey-io/valkey-glide/go/glide/utils"
)

const (
// LIMIT subcommand string to include in the SORT and SORT_RO commands.
LIMIT_COMMAND_STRING = "LIMIT"
// ALPHA subcommand string to include in the SORT and SORT_RO commands.
ALPHA_COMMAND_STRING = "ALPHA"
// BY subcommand string to include in the SORT and SORT_RO commands.
// Supported in cluster mode since Valkey version 8.0 and above.
BY_COMMAND_STRING = "BY"
// GET subcommand string to include in the SORT and SORT_RO commands.
GET_COMMAND_STRING = "GET"
)

// Limit struct represents the range of elements to retrieve
// The LIMIT argument is commonly used to specify a subset of results from the matching elements, similar to the
// LIMIT clause in SQL (e.g., `SELECT LIMIT offset, count`).
type Limit struct {
// The starting position of the range, zero based.
Offset int64
// The maximum number of elements to include in the range. A negative count returns all elementsnfrom the offset.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider moving docs from lines 25 and 27 to SetLimit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Count int64
}

// OrderBy specifies the order to sort the elements. Can be ASC (ascending) or DESC(descending).
type OrderBy string

const (
ASC OrderBy = "ASC"
DESC OrderBy = "DESC"
)

// SortOptions struct combines both the base options and additional sorting options
type SortOptions struct {
// Limit Limits the range of elements
Limit *Limit

// OrderBy sets the order to sort by (ASC or DESC)
OrderBy OrderBy

// IsAlpha determines whether to sort lexicographically (true) or numerically (false)
IsAlpha bool

// ByPattern - a pattern to sort by external keys instead of by the elements stored at the key themselves. The
// pattern should contain an asterisk (*) as a placeholder for the element values, where the value
// from the key replaces the asterisk to create the key name. For example, if key
// contains IDs of objects, byPattern can be used to sort these IDs based on an
// attribute of the objects, like their weights or timestamps.
// Supported in cluster mode since Valkey version 8.0 and above.
ByPattern string

// A pattern used to retrieve external keys' values, instead of the elements at key.
// The pattern should contain an asterisk (*) as a placeholder for the element values, where the
// value from key replaces the asterisk to create the key name. This
// allows the sorted elements to be transformed based on the related keys values. For example, if
// key< contains IDs of users, getPatterns can be used to retrieve
// specific attributes of these users, such as their names or email addresses. E.g., if
// getPatterns is name_*, the command will return the values of the keys
// name_&lt;element&gt; for each sorted element. Multiple getPatterns
// arguments can be provided to retrieve multiple attributes. The special value # can
// be used to include the actual element from key being sorted. If not provided, only
// the sorted elements themselves are returned.
// Supported in cluster mode since Valkey version 8.0 and above.
GetPatterns []string // List of patterns to retrieve external keys' values
Copy link
Collaborator

Choose a reason for hiding this comment

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

Move all docs to the corresponding setters. I afraid doc of private fields won't be visible to a user.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

}

func NewSortOptions() *SortOptions {
return &SortOptions{
OrderBy: ASC, // Default order is ascending
IsAlpha: false, // Default is numeric sorting
}
}

func (opts *SortOptions) SetLimit(offset, count int64) *SortOptions {
opts.Limit = &Limit{Offset: offset, Count: count}
return opts
}

func (opts *SortOptions) SetOrderBy(order OrderBy) *SortOptions {
opts.OrderBy = order
return opts
}

func (opts *SortOptions) SetIsAlpha(isAlpha bool) *SortOptions {
opts.IsAlpha = isAlpha
return opts
}

func (opts *SortOptions) SetByPattern(byPattern string) *SortOptions {
opts.ByPattern = byPattern
return opts
}

func (opts *SortOptions) AddGetPattern(getPattern string) *SortOptions {
opts.GetPatterns = append(opts.GetPatterns, getPattern)
return opts
}

// ToArgs creates the arguments to be used in SORT and SORT_RO commands.
func (opts *SortOptions) ToArgs() []string {
var args []string

if opts.Limit != nil {
args = append(args, LIMIT_COMMAND_STRING, utils.IntToString(opts.Limit.Offset), utils.IntToString(opts.Limit.Count))
}

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

if opts.IsAlpha {
args = append(args, ALPHA_COMMAND_STRING)
}

if opts.ByPattern != "" {
args = append(args, BY_COMMAND_STRING, opts.ByPattern)
}

for _, getPattern := range opts.GetPatterns {
args = append(args, GET_COMMAND_STRING, getPattern)
}
return args
}
Loading
Loading