mirror of
https://github.com/EchoVault/SugarDB.git
synced 2025-10-05 16:06:57 +08:00
959 lines
29 KiB
Go
959 lines
29 KiB
Go
// Copyright 2024 Kelvin Clement Mwinuka
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package echovault
|
|
|
|
import (
|
|
"github.com/echovault/echovault/internal"
|
|
"strconv"
|
|
)
|
|
|
|
// ZADDOptions allows you to modify the effects of the ZADD command.
|
|
//
|
|
// "NX" only adds the member if it currently does not exist in the sorted set. This flag is mutually exclusive with the
|
|
// "GT" and "LT" flags. The "NX" flag takes higher priority than the "XX" flag.
|
|
//
|
|
// "XX" only updates the scores of members that exist in the sorted set.
|
|
//
|
|
// "GT"" only updates the score if the new score is greater than the current score. The "GT" flat is higher priority
|
|
// than the "LT" flag.
|
|
//
|
|
// "LT" only updates the score if the new score is less than the current score.
|
|
//
|
|
// "CH" modifies the result to return total number of members changed + added, instead of only new members added. When
|
|
// this flag is set to true, only the number of members that have been updated will be returned.
|
|
//
|
|
// "INCR" modifies the command to act like ZINCRBY, only one score/member pair can be specified in this mode. When this flag
|
|
// is provided, only one member/score pair is allowed.
|
|
type ZADDOptions struct {
|
|
NX bool
|
|
XX bool
|
|
GT bool
|
|
LT bool
|
|
CH bool
|
|
INCR bool
|
|
}
|
|
|
|
// ZINTEROptions allows you to modify the result of the ZINTER* and ZUNION* family of commands
|
|
//
|
|
// Weights is a slice of float64 that determines the weights of each sorted set in the aggregation command.
|
|
// each weight will be each weight will be applied to the sorted set at the corresponding index.
|
|
// The weight value is multiplied by each member of corresponding sorted set.
|
|
//
|
|
// Aggregate determines how the scores are combined AFTER the weights are applied. There are 3 possible vales,
|
|
// "MIN" will select the minimum score element to place in the resulting sorted set.
|
|
// "MAX" will select the maximum score element to place in the resulting sorted set.
|
|
// "SUM" will add all the scores to place in the resulting sorted set.
|
|
//
|
|
// WithScores determines whether to return the scores of the resulting set.
|
|
type ZINTEROptions struct {
|
|
Weights []float64
|
|
Aggregate string
|
|
WithScores bool
|
|
}
|
|
type ZINTERSTOREOptions ZINTEROptions
|
|
type ZUNIONOptions ZINTEROptions
|
|
type ZUNIONSTOREOptions ZINTEROptions
|
|
|
|
// ZMPOPOptions allows you to modify the result of the ZMPOP command.
|
|
//
|
|
// Min instructs EchoVault to pop the minimum score elements. Min is higher priority than Max.
|
|
//
|
|
// Max instructs EchoVault to pop the maximum score elements.
|
|
//
|
|
// Count specifies the number of elements to pop.
|
|
type ZMPOPOptions struct {
|
|
Min bool
|
|
Max bool
|
|
Count uint
|
|
}
|
|
|
|
// ZRANGEOptions allows you to modify the effects of the ZRANGE* family of commands.
|
|
//
|
|
// WithScores specifies whether to return the associated scores.
|
|
//
|
|
// ByScore compares the elements by score within the numerical ranges specified. ByScore is higher priority than ByLex.
|
|
//
|
|
// ByLex returns the elements within the lexicographical ranges specified.
|
|
//
|
|
// Offset specifies the offset to from which to start the ZRANGE process.
|
|
//
|
|
// Count specifies the number of elements to return.
|
|
type ZRANGEOptions struct {
|
|
WithScores bool
|
|
ByScore bool
|
|
ByLex bool
|
|
Offset uint
|
|
Count uint
|
|
}
|
|
type ZRANGESTOREOptions ZRANGEOptions
|
|
|
|
func buildMemberScoreMap(arr [][]string, withscores bool) (map[string]float64, error) {
|
|
result := make(map[string]float64, len(arr))
|
|
for _, entry := range arr {
|
|
if withscores {
|
|
score, err := strconv.ParseFloat(entry[1], 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result[entry[0]] = score
|
|
continue
|
|
}
|
|
result[entry[0]] = 0
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func buildIntegerScoreMap(arr [][]string, withscores bool) (map[int]float64, error) {
|
|
result := make(map[int]float64, len(arr))
|
|
for _, entry := range arr {
|
|
rank, err := strconv.Atoi(entry[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result[rank] = 0
|
|
if withscores {
|
|
score, err := strconv.ParseFloat(entry[1], 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result[rank] = score
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// ZADD adds member(s) to a sorted set. If the sorted set does not exist, a new sorted set is created with the
|
|
// member(s).
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `key` - string - the key to update.
|
|
//
|
|
// `members` - map[string]float64 - a map of the members to add. The key is the string and the value is a float64 score.
|
|
//
|
|
// `options` - ZADDOptions
|
|
//
|
|
// Returns: The number of members added, or the number of members updated in the "CH" flag is true.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "GT/LT flags not allowed if NX flag is provided" - when GT/LT flags are provided alongside NX flag.
|
|
//
|
|
// "cannot pass more than one score/member pair when INCR flag is provided" - when INCR flag is provided and more than
|
|
// one member-score pair is provided.
|
|
//
|
|
// "value at <key> is not a sorted set" - when the provided key exists but is not a sorted set
|
|
func (server *EchoVault) ZADD(key string, members map[string]float64, options ZADDOptions) (int, error) {
|
|
cmd := []string{"ZADD", key}
|
|
|
|
switch {
|
|
case options.NX:
|
|
cmd = append(cmd, "NX")
|
|
case options.XX:
|
|
cmd = append(cmd, "XX")
|
|
}
|
|
|
|
switch {
|
|
case options.GT:
|
|
cmd = append(cmd, "GT")
|
|
case options.LT:
|
|
cmd = append(cmd, "LT")
|
|
}
|
|
|
|
if options.CH {
|
|
cmd = append(cmd, "CH")
|
|
}
|
|
|
|
if options.INCR {
|
|
cmd = append(cmd, "INCR")
|
|
}
|
|
|
|
for member, score := range members {
|
|
cmd = append(cmd, []string{strconv.FormatFloat(score, 'f', -1, 64), member}...)
|
|
}
|
|
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return internal.ParseIntegerResponse(b)
|
|
}
|
|
|
|
// ZCARD returns the cardinality of the sorted set.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `key` - string - the key of the sorted set.
|
|
//
|
|
// Returns: The cardinality of the sorted set. Returns 0 if the keys does not exist.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when the provided key exists but is not a sorted set
|
|
func (server *EchoVault) ZCARD(key string) (int, error) {
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"ZCARD", key}), nil, false, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return internal.ParseIntegerResponse(b)
|
|
}
|
|
|
|
// ZCOUNT returns the number of elements in the sorted set key with scores in the range of min and max.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `key` - string - the key of the sorted set.
|
|
//
|
|
// `min` - float64 - the minimum score boundary.
|
|
//
|
|
// `max` - float64 - the maximum score boundary.
|
|
//
|
|
// Returns: The number of members with scores in the given range. Returns 0 if the keys does not exist.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when the provided key exists but is not a sorted set
|
|
func (server *EchoVault) ZCOUNT(key string, min, max float64) (int, error) {
|
|
cmd := []string{
|
|
"ZCOUNT",
|
|
key,
|
|
strconv.FormatFloat(min, 'f', -1, 64),
|
|
strconv.FormatFloat(max, 'f', -1, 64),
|
|
}
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return internal.ParseIntegerResponse(b)
|
|
}
|
|
|
|
// ZDIFF Calculates the difference between the sorted sets and returns the resulting sorted set.
|
|
// All keys that are non-existed are skipped.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `withscores` - bool - whether to return the results with scores or not. If false, all the returned scores
|
|
// will be 0.
|
|
//
|
|
// `keys` - []string - the keys to the sorted sets to be used in calculating the difference.
|
|
//
|
|
// Returns: A map representing the resulting sorted set where the key is the member and the value is a float64 score.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when the provided key exists but is not a sorted set.
|
|
func (server *EchoVault) ZDIFF(withscores bool, keys ...string) (map[string]float64, error) {
|
|
cmd := append([]string{"ZDIFF"}, keys...)
|
|
if withscores {
|
|
cmd = append(cmd, "WITHSCORES")
|
|
}
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
arr, err := internal.ParseNestedStringArrayResponse(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return buildMemberScoreMap(arr, withscores)
|
|
}
|
|
|
|
// ZDIFFSTORE Calculates the difference between the sorted sets and stores the resulting sorted set at 'destination'.
|
|
// Non-existent keys will be skipped.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `destination` - string - the destination key at which to store the resulting sorted set.
|
|
//
|
|
// `keys` - []string - the keys to the sorted sets to be used in calculating the difference.
|
|
//
|
|
// Returns: The cardinality of the new sorted set.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when a key exists but is not a sorted set.
|
|
func (server *EchoVault) ZDIFFSTORE(destination string, keys ...string) (int, error) {
|
|
cmd := append([]string{"ZDIFFSTORE", destination}, keys...)
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return internal.ParseIntegerResponse(b)
|
|
}
|
|
|
|
// ZINTER Calculates the intersection between the sorted sets and returns the resulting sorted set.
|
|
// if any of the keys provided are non-existent, an empty map is returned.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `keys` - []string - the keys to the sorted sets to be used in calculating the intersection.
|
|
//
|
|
// `options` - ZINTEROptions
|
|
//
|
|
// Returns: A map representing the resulting sorted set where the key is the member and the value is a float64 score.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when the provided key exists but is not a sorted set.
|
|
func (server *EchoVault) ZINTER(keys []string, options ZINTEROptions) (map[string]float64, error) {
|
|
cmd := append([]string{"ZINTER"}, keys...)
|
|
|
|
if len(options.Weights) > 0 {
|
|
cmd = append(cmd, "WEIGHTS")
|
|
for i := 0; i < len(options.Weights); i++ {
|
|
cmd = append(cmd, strconv.FormatFloat(options.Weights[i], 'f', -1, 64))
|
|
}
|
|
}
|
|
|
|
if options.Aggregate != "" {
|
|
cmd = append(cmd, []string{"AGGREGATE", options.Aggregate}...)
|
|
}
|
|
|
|
if options.WithScores {
|
|
cmd = append(cmd, "WITHSCORES")
|
|
}
|
|
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
arr, err := internal.ParseNestedStringArrayResponse(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return buildMemberScoreMap(arr, options.WithScores)
|
|
}
|
|
|
|
// ZINTERSTORE Calculates the intersection between the sorted sets and stores the resulting sorted set at 'destination'.
|
|
// If any of the keys does not exist, the operation is abandoned.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `destination` - string - the destination key at which to store the resulting sorted set.
|
|
//
|
|
// `keys` - []string - the keys to the sorted sets to be used in calculating the intersection.
|
|
//
|
|
// `options` - ZINTERSTOREOptions
|
|
//
|
|
// Returns: The cardinality of the new sorted set.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when a key exists but is not a sorted set.
|
|
func (server *EchoVault) ZINTERSTORE(destination string, keys []string, options ZINTERSTOREOptions) (int, error) {
|
|
cmd := append([]string{"ZINTERSTORE", destination}, keys...)
|
|
|
|
if len(options.Weights) > 0 {
|
|
cmd = append(cmd, "WEIGHTS")
|
|
for _, weight := range options.Weights {
|
|
cmd = append(cmd, strconv.FormatFloat(float64(weight), 'f', -1, 64))
|
|
}
|
|
}
|
|
|
|
if options.Aggregate != "" {
|
|
cmd = append(cmd, []string{"AGGREGATE", options.Aggregate}...)
|
|
}
|
|
|
|
if options.WithScores {
|
|
cmd = append(cmd, "WITHSCORES")
|
|
}
|
|
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return internal.ParseIntegerResponse(b)
|
|
}
|
|
|
|
// ZUNION Calculates the union between the sorted sets and returns the resulting sorted set.
|
|
// if any of the keys provided are non-existent, an error is returned.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `keys` - []string - the keys to the sorted sets to be used in calculating the union.
|
|
//
|
|
// `options` - ZUNIONOptions
|
|
//
|
|
// Returns: A map representing the resulting sorted set where the key is the member and the value is a float64 score.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when the provided key exists but is not a sorted set.
|
|
func (server *EchoVault) ZUNION(keys []string, options ZUNIONOptions) (map[string]float64, error) {
|
|
cmd := append([]string{"ZUNION"}, keys...)
|
|
|
|
if len(options.Weights) > 0 {
|
|
cmd = append(cmd, "WEIGHTS")
|
|
for _, weight := range options.Weights {
|
|
cmd = append(cmd, strconv.FormatFloat(float64(weight), 'f', -1, 64))
|
|
}
|
|
}
|
|
|
|
if options.Aggregate != "" {
|
|
cmd = append(cmd, []string{"AGGREGATE", options.Aggregate}...)
|
|
}
|
|
|
|
if options.WithScores {
|
|
cmd = append(cmd, "WITHSCORES")
|
|
}
|
|
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
arr, err := internal.ParseNestedStringArrayResponse(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return buildMemberScoreMap(arr, options.WithScores)
|
|
}
|
|
|
|
// ZUNIONSTORE Calculates the union between the sorted sets and stores the resulting sorted set at 'destination'.
|
|
// Non-existent keys will be skipped.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `destination` - string - the destination key at which to store the resulting sorted set.
|
|
//
|
|
// `keys` - []string - the keys to the sorted sets to be used in calculating the union.
|
|
//
|
|
// `options` - ZUNIONSTOREOptions
|
|
//
|
|
// Returns: The cardinality of the new sorted set.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when a key exists but is not a sorted set.
|
|
func (server *EchoVault) ZUNIONSTORE(destination string, keys []string, options ZUNIONSTOREOptions) (int, error) {
|
|
cmd := append([]string{"ZUNIONSTORE", destination}, keys...)
|
|
|
|
if len(options.Weights) > 0 {
|
|
cmd = append(cmd, "WEIGHTS")
|
|
for _, weight := range options.Weights {
|
|
cmd = append(cmd, strconv.FormatFloat(float64(weight), 'f', -1, 64))
|
|
}
|
|
}
|
|
|
|
if options.Aggregate != "" {
|
|
cmd = append(cmd, []string{"AGGREGATE", options.Aggregate}...)
|
|
}
|
|
|
|
if options.WithScores {
|
|
cmd = append(cmd, "WITHSCORES")
|
|
}
|
|
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return internal.ParseIntegerResponse(b)
|
|
}
|
|
|
|
// ZINCRBY Increments the score of the specified sorted set's member by the increment. If the member does not exist, it is created.
|
|
// If the key does not exist, it is created with new sorted set and the member added with the increment as its score.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `key` - string - the keys to the sorted set.
|
|
//
|
|
// `increment` - float64 - the increment to apply to the member's score.
|
|
//
|
|
// `member` - string - the member to increment.
|
|
//
|
|
// Returns: The cardinality of the new sorted set.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when a key exists but is not a sorted set.
|
|
func (server *EchoVault) ZINCRBY(key string, increment float64, member string) (float64, error) {
|
|
cmd := []string{"ZINCRBY", key, strconv.FormatFloat(increment, 'f', -1, 64), member}
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
f, err := internal.ParseFloatResponse(b)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return f, nil
|
|
}
|
|
|
|
// ZMPOP Pop a 'count' elements from multiple sorted sets. MIN or MAX determines whether to pop elements with the lowest
|
|
// or highest scores respectively.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `keys` - []string - the keys to the sorted sets to pop from.
|
|
//
|
|
// `options` - ZMPOPOptions
|
|
//
|
|
// Returns: A 2-dimensional slice where each slice contains the member and score at the 0 and 1 indices respectively.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when a key exists but is not a sorted set.
|
|
func (server *EchoVault) ZMPOP(keys []string, options ZMPOPOptions) ([][]string, error) {
|
|
cmd := append([]string{"ZMPOP"}, keys...)
|
|
|
|
switch {
|
|
case options.Min:
|
|
cmd = append(cmd, "MIN")
|
|
case options.Max:
|
|
cmd = append(cmd, "MAX")
|
|
default:
|
|
cmd = append(cmd, "MIN")
|
|
}
|
|
|
|
switch {
|
|
case options.Count != 0:
|
|
cmd = append(cmd, []string{"COUNT", strconv.Itoa(int(options.Count))}...)
|
|
default:
|
|
cmd = append(cmd, []string{"COUNT", strconv.Itoa(1)}...)
|
|
}
|
|
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return internal.ParseNestedStringArrayResponse(b)
|
|
}
|
|
|
|
// ZMSCORE Returns the associated scores of the specified member in the sorted set.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `key` - string - The keys to the sorted set.
|
|
//
|
|
// `members` - ...string - Them members whose scores will be returned.
|
|
//
|
|
// Returns: A slice of interface{} with the result scores. For existing members, the entry will be represented by a string.
|
|
// For non-existent members, the score will be nil. You will have to format the string score into a float64 if you
|
|
// would like to use it as a float64.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when a key exists but is not a sorted set.
|
|
func (server *EchoVault) ZMSCORE(key string, members ...string) ([]interface{}, error) {
|
|
cmd := []string{"ZMSCORE", key}
|
|
for _, member := range members {
|
|
cmd = append(cmd, member)
|
|
}
|
|
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
arr, err := internal.ParseStringArrayResponse(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
scores := make([]interface{}, len(arr))
|
|
for i, e := range arr {
|
|
if e == "" {
|
|
scores[i] = nil
|
|
continue
|
|
}
|
|
score, err := strconv.ParseFloat(e, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
scores[i] = score
|
|
}
|
|
|
|
return scores, nil
|
|
}
|
|
|
|
// ZLEXCOUNT returns the number of elements in the sorted set within the lexicographical range between min and max.
|
|
// This function only returns a non-zero value if all the members have the same score.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `key` - string - the key of the sorted set.
|
|
//
|
|
// `min` - string - the minimum lex boundary.
|
|
//
|
|
// `max` - string - the maximum lex boundary.
|
|
//
|
|
// Returns: The number of members within the given lexicographical range.
|
|
// Returns 0 if the keys does not exist or all the members don't have the same score.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when the provided key exists but is not a sorted set
|
|
func (server *EchoVault) ZLEXCOUNT(key, min, max string) (int, error) {
|
|
cmd := []string{"ZLEXCOUNT", key, min, max}
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return internal.ParseIntegerResponse(b)
|
|
}
|
|
|
|
// ZPOPMAX Removes and returns 'count' number of members in the sorted set with the highest scores. Default count is 1.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `key` - string - The keys to the sorted set.
|
|
//
|
|
// `count` - uint - The number of max elements to pop. If a count of 0 is provided, it will be ignored
|
|
// and 1 element will be popped instead.
|
|
//
|
|
// Returns: A 2-dimensional slice where each slice contains a member and its score at the 0 and 1 indices respectively.
|
|
// The returned scores are strings. If you'd like to use them as float64 or another numeric type, you will have to
|
|
// format them.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when a key exists but is not a sorted set.
|
|
func (server *EchoVault) ZPOPMAX(key string, count uint) ([][]string, error) {
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"ZPOPMAX", key, strconv.Itoa(int(count))}), nil, false, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return internal.ParseNestedStringArrayResponse(b)
|
|
}
|
|
|
|
// ZPOPMIN Removes and returns 'count' number of members in the sorted set with the lowest scores. Default count is 1.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `key` - string - The keys to the sorted set.
|
|
//
|
|
// `count` - uint - The number of min elements to pop. If a count of 0 is provided, it will be ignored
|
|
// and 1 element will be popped instead.
|
|
//
|
|
// Returns: A 2-dimensional slice where each slice contains a member and its score at the 0 and 1 indices respectively.
|
|
// The returned scores are strings. If you'd like to use them as float64 or another numeric type, you will have to
|
|
// format them.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when a key exists but is not a sorted set.
|
|
func (server *EchoVault) ZPOPMIN(key string, count uint) ([][]string, error) {
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"ZPOPMIN", key, strconv.Itoa(int(count))}), nil, false, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return internal.ParseNestedStringArrayResponse(b)
|
|
}
|
|
|
|
// ZRANDMEMBER Returns a list of length equivalent to 'count' containing random members of the sorted set.
|
|
// If count is negative, repeated elements are allowed. If count is positive, the returned elements will be distinct.
|
|
// The default count is 1. If a count of 0 is passed, it will be ignored.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `key` - string - The keys to the sorted set.
|
|
//
|
|
// `count` - int - The number of random members to return. If the absolute value of count is greater than the
|
|
// sorted set's cardinality, the whole sorted set will be returned.
|
|
//
|
|
// `withscores` - bool - Whether to return the members' associated scores. If this is false, the returned scores will
|
|
// be 0.
|
|
//
|
|
// Returns: A 2-dimensional slice where each slice contains a member and its score at the 0 and 1 indices respectively.
|
|
// The returned scores are strings. If you'd like to use them as float64 or another numeric type, you will have to
|
|
// format them.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when a key exists but is not a sorted set.
|
|
func (server *EchoVault) ZRANDMEMBER(key string, count int, withscores bool) ([][]string, error) {
|
|
cmd := []string{"ZRANDMEMBER", key}
|
|
if count != 0 {
|
|
cmd = append(cmd, strconv.Itoa(count))
|
|
}
|
|
if withscores {
|
|
cmd = append(cmd, "WITHSCORES")
|
|
}
|
|
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return internal.ParseNestedStringArrayResponse(b)
|
|
}
|
|
|
|
// ZRANK Returns the rank of the specified member in the sorted set. The rank is derived from organising the members
|
|
// in descending order of score.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `key` - string - The keys to the sorted set.
|
|
//
|
|
// `member` - string - The member whose rank will be returned.
|
|
//
|
|
// `withscores` - bool - Whether to return the member associated scores. If this is false, the returned score will
|
|
// be 0.
|
|
//
|
|
// Returns: A map of map[string]float64 where the key is the member and the value is the score.
|
|
// If the member does not exist in the sorted set, an empty map is returned.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when a key exists but is not a sorted set.
|
|
func (server *EchoVault) ZRANK(key string, member string, withscores bool) (map[int]float64, error) {
|
|
cmd := []string{"ZRANK", key, member}
|
|
if withscores {
|
|
cmd = append(cmd, "WITHSCORES")
|
|
}
|
|
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
arr, err := internal.ParseStringArrayResponse(b)
|
|
|
|
if len(arr) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
s, err := strconv.Atoi(arr[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res := map[int]float64{s: 0}
|
|
|
|
if withscores {
|
|
f, err := strconv.ParseFloat(arr[1], 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res[s] = f
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
// ZREVRANK works the same as ZRANK but derives the member's rank based on ascending order of
|
|
// the members' scores.
|
|
func (server *EchoVault) ZREVRANK(key string, member string, withscores bool) (map[int]float64, error) {
|
|
cmd := []string{"ZREVRANK", key, member}
|
|
if withscores {
|
|
cmd = append(cmd, "WITHSCORES")
|
|
}
|
|
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
arr, err := internal.ParseNestedStringArrayResponse(b)
|
|
|
|
return buildIntegerScoreMap(arr, withscores)
|
|
}
|
|
|
|
// ZSCORE Returns the score of the member in the sorted set.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `key` - string - The keys to the sorted set.
|
|
//
|
|
// `member` - string - The member whose rank will be returned.
|
|
//
|
|
// Returns: An interface representing the score of the member. If the member does not exist in the sorted set, nil is
|
|
// returned. Otherwise, a float64 is returned.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when a key exists but is not a sorted set.
|
|
func (server *EchoVault) ZSCORE(key string, member string) (interface{}, error) {
|
|
cmd := []string{"ZSCORE", key, member}
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
isNil, err := internal.ParseNilResponse(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if isNil {
|
|
return nil, nil
|
|
}
|
|
|
|
score, err := internal.ParseFloatResponse(b)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return score, nil
|
|
}
|
|
|
|
// ZREM Removes the listed members from the sorted set.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `key` - string - The keys to the sorted set.
|
|
//
|
|
// `members` - ...string - The members to remove.
|
|
//
|
|
// Returns: The number of elements that were successfully removed.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when a key exists but is not a sorted set.
|
|
func (server *EchoVault) ZREM(key string, members ...string) (int, error) {
|
|
cmd := []string{"ZREM", key}
|
|
for _, member := range members {
|
|
cmd = append(cmd, member)
|
|
}
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return internal.ParseIntegerResponse(b)
|
|
}
|
|
|
|
// ZREMRANGEBYSCORE Removes the elements whose scores are in the range between min and max.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `key` - string - The keys to the sorted set.
|
|
//
|
|
// `min` - float64 - The minimum score boundary.
|
|
//
|
|
// `max` - float64 - The maximum score boundary.
|
|
//
|
|
// Returns: The number of elements that were successfully removed.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when a key exists but is not a sorted set.
|
|
func (server *EchoVault) ZREMRANGEBYSCORE(key string, min float64, max float64) (int, error) {
|
|
cmd := []string{
|
|
"ZREMRANGEBYSCORE",
|
|
key,
|
|
strconv.FormatFloat(min, 'f', -1, 64),
|
|
strconv.FormatFloat(max, 'f', -1, 64),
|
|
}
|
|
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return internal.ParseIntegerResponse(b)
|
|
}
|
|
|
|
// ZRANGE Returns the range of elements in the sorted set.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `key` - string - The keys to the sorted set.
|
|
//
|
|
// `start` - string - The minimum boundary.
|
|
//
|
|
// `stop` - string - The maximum boundary.
|
|
//
|
|
// `options` - ZRANGEOptions
|
|
//
|
|
// Returns: A map of map[string]float64 where the key is the member and the value is its score.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when a key exists but is not a sorted set.
|
|
func (server *EchoVault) ZRANGE(key, start, stop string, options ZRANGEOptions) (map[string]float64, error) {
|
|
cmd := []string{"ZRANGE", key, start, stop}
|
|
|
|
switch {
|
|
case options.ByScore:
|
|
cmd = append(cmd, "BYSCORE")
|
|
case options.ByLex:
|
|
cmd = append(cmd, "BYLEX")
|
|
default:
|
|
cmd = append(cmd, "BYSCORE")
|
|
}
|
|
|
|
if options.WithScores {
|
|
cmd = append(cmd, "WITHSCORES")
|
|
}
|
|
|
|
if options.Offset != 0 && options.Count != 0 {
|
|
cmd = append(cmd, []string{"LIMIT", strconv.Itoa(int(options.Offset)), strconv.Itoa(int(options.Count))}...)
|
|
}
|
|
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
arr, err := internal.ParseNestedStringArrayResponse(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return buildMemberScoreMap(arr, options.WithScores)
|
|
}
|
|
|
|
// ZRANGESTORE Works like ZRANGE but stores the result in at the 'destination' key.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `destination` - string - The key at which to store the new sorted set
|
|
//
|
|
// `key` - string - The keys to the sorted set.
|
|
//
|
|
// `start` - string - The minimum boundary.
|
|
//
|
|
// `stop` - string - The maximum boundary.
|
|
//
|
|
// `options` - ZRANGESTOREOptions
|
|
//
|
|
// Returns: The cardinality of the new sorted set.
|
|
//
|
|
// Errors:
|
|
//
|
|
// "value at <key> is not a sorted set" - when a key exists but is not a sorted set.
|
|
func (server *EchoVault) ZRANGESTORE(destination, source, start, stop string, options ZRANGESTOREOptions) (int, error) {
|
|
cmd := []string{"ZRANGESTORE", destination, source, start, stop}
|
|
|
|
switch {
|
|
case options.ByScore:
|
|
cmd = append(cmd, "BYSCORE")
|
|
case options.ByLex:
|
|
cmd = append(cmd, "BYLEX")
|
|
default:
|
|
cmd = append(cmd, "BYSCORE")
|
|
}
|
|
|
|
if options.Offset != 0 && options.Count != 0 {
|
|
cmd = append(cmd, []string{"LIMIT", strconv.Itoa(int(options.Offset)), strconv.Itoa(int(options.Count))}...)
|
|
}
|
|
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return internal.ParseIntegerResponse(b)
|
|
}
|