mirror of
https://github.com/EchoVault/SugarDB.git
synced 2025-10-04 23:52:42 +08:00
Implemented tests for sorted set API
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -28,17 +28,17 @@ type Value string
|
|||||||
|
|
||||||
type Score float64
|
type Score float64
|
||||||
|
|
||||||
// MemberObject is the shape of the object as it's stored in the map that represents the set
|
// MemberObject is the shape of the object as it's stored in the map that represents the Set
|
||||||
type MemberObject struct {
|
type MemberObject struct {
|
||||||
value Value
|
Value Value
|
||||||
score Score
|
Score Score
|
||||||
exists bool
|
Exists bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// MemberParam is the shape of the object passed as a parameter to NewSortedSet and the Add method
|
// MemberParam is the shape of the object passed as a parameter to NewSortedSet and the Add method
|
||||||
type MemberParam struct {
|
type MemberParam struct {
|
||||||
value Value
|
Value Value
|
||||||
score Score
|
Score Score
|
||||||
}
|
}
|
||||||
|
|
||||||
type SortedSet struct {
|
type SortedSet struct {
|
||||||
@@ -50,17 +50,17 @@ func NewSortedSet(members []MemberParam) *SortedSet {
|
|||||||
members: make(map[Value]MemberObject),
|
members: make(map[Value]MemberObject),
|
||||||
}
|
}
|
||||||
for _, m := range members {
|
for _, m := range members {
|
||||||
s.members[m.value] = MemberObject{
|
s.members[m.Value] = MemberObject{
|
||||||
value: m.value,
|
Value: m.Value,
|
||||||
score: m.score,
|
Score: m.Score,
|
||||||
exists: true,
|
Exists: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (set *SortedSet) Contains(m Value) bool {
|
func (set *SortedSet) Contains(m Value) bool {
|
||||||
return set.members[m].exists
|
return set.members[m].Exists
|
||||||
}
|
}
|
||||||
|
|
||||||
func (set *SortedSet) Get(v Value) MemberObject {
|
func (set *SortedSet) Get(v Value) MemberObject {
|
||||||
@@ -89,11 +89,11 @@ func (set *SortedSet) GetRandom(count int) []MemberParam {
|
|||||||
for i := 0; i < internal.AbsInt(count); {
|
for i := 0; i < internal.AbsInt(count); {
|
||||||
n = rand.Intn(len(members))
|
n = rand.Intn(len(members))
|
||||||
if !slices.ContainsFunc(res, func(m MemberParam) bool {
|
if !slices.ContainsFunc(res, func(m MemberParam) bool {
|
||||||
return m.value == members[n].value
|
return m.Value == members[n].Value
|
||||||
}) {
|
}) {
|
||||||
res = append(res, members[n])
|
res = append(res, members[n])
|
||||||
slices.DeleteFunc(members, func(m MemberParam) bool {
|
slices.DeleteFunc(members, func(m MemberParam) bool {
|
||||||
return m.value == members[n].value
|
return m.Value == members[n].Value
|
||||||
})
|
})
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
@@ -107,8 +107,8 @@ func (set *SortedSet) GetAll() []MemberParam {
|
|||||||
var res []MemberParam
|
var res []MemberParam
|
||||||
for k, v := range set.members {
|
for k, v := range set.members {
|
||||||
res = append(res, MemberParam{
|
res = append(res, MemberParam{
|
||||||
value: k,
|
Value: k,
|
||||||
score: v.score,
|
Score: v.Score,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
@@ -141,31 +141,31 @@ func (set *SortedSet) AddOrUpdate(
|
|||||||
return 0, errors.New("cannot use GT or LT when update policy is NX")
|
return 0, errors.New("cannot use GT or LT when update policy is NX")
|
||||||
}
|
}
|
||||||
if strings.EqualFold(inc, "incr") && len(members) != 1 {
|
if strings.EqualFold(inc, "incr") && len(members) != 1 {
|
||||||
return 0, errors.New("INCR can only be used with one member/score pair")
|
return 0, errors.New("INCR can only be used with one member/Score pair")
|
||||||
}
|
}
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
|
|
||||||
if strings.EqualFold(inc, "incr") {
|
if strings.EqualFold(inc, "incr") {
|
||||||
for _, m := range members {
|
for _, m := range members {
|
||||||
if !set.Contains(m.value) {
|
if !set.Contains(m.Value) {
|
||||||
// If the member is not contained, add it with the increment as its score
|
// If the member is not contained, add it with the increment as its Score
|
||||||
set.members[m.value] = MemberObject{
|
set.members[m.Value] = MemberObject{
|
||||||
value: m.value,
|
Value: m.Value,
|
||||||
score: m.score,
|
Score: m.Score,
|
||||||
exists: true,
|
Exists: true,
|
||||||
}
|
}
|
||||||
// Always add count because this is the addition of a new element
|
// Always add count because this is the addition of a new element
|
||||||
count += 1
|
count += 1
|
||||||
return count, err
|
return count, err
|
||||||
}
|
}
|
||||||
if slices.Contains([]Score{Score(math.Inf(-1)), Score(math.Inf(1))}, set.members[m.value].score) {
|
if slices.Contains([]Score{Score(math.Inf(-1)), Score(math.Inf(1))}, set.members[m.Value].Score) {
|
||||||
return count, errors.New("cannot increment -inf or +inf")
|
return count, errors.New("cannot increment -inf or +inf")
|
||||||
}
|
}
|
||||||
set.members[m.value] = MemberObject{
|
set.members[m.Value] = MemberObject{
|
||||||
value: m.value,
|
Value: m.Value,
|
||||||
score: set.members[m.value].score + m.score,
|
Score: set.members[m.Value].Score + m.Score,
|
||||||
exists: true,
|
Exists: true,
|
||||||
}
|
}
|
||||||
if strings.EqualFold(ch, "ch") {
|
if strings.EqualFold(ch, "ch") {
|
||||||
count += 1
|
count += 1
|
||||||
@@ -177,11 +177,11 @@ func (set *SortedSet) AddOrUpdate(
|
|||||||
for _, m := range members {
|
for _, m := range members {
|
||||||
if strings.EqualFold(policy, "xx") {
|
if strings.EqualFold(policy, "xx") {
|
||||||
// Only update existing elements, do not add new elements
|
// Only update existing elements, do not add new elements
|
||||||
if set.Contains(m.value) {
|
if set.Contains(m.Value) {
|
||||||
set.members[m.value] = MemberObject{
|
set.members[m.Value] = MemberObject{
|
||||||
value: m.value,
|
Value: m.Value,
|
||||||
score: compareScores(set.members[m.value].score, m.score, comp),
|
Score: compareScores(set.members[m.Value].Score, m.Score, comp),
|
||||||
exists: true,
|
Exists: true,
|
||||||
}
|
}
|
||||||
if strings.EqualFold(ch, "ch") {
|
if strings.EqualFold(ch, "ch") {
|
||||||
count += 1
|
count += 1
|
||||||
@@ -191,24 +191,24 @@ func (set *SortedSet) AddOrUpdate(
|
|||||||
}
|
}
|
||||||
if strings.EqualFold(policy, "nx") {
|
if strings.EqualFold(policy, "nx") {
|
||||||
// Only add new elements, do not update existing elements
|
// Only add new elements, do not update existing elements
|
||||||
if !set.Contains(m.value) {
|
if !set.Contains(m.Value) {
|
||||||
set.members[m.value] = MemberObject{
|
set.members[m.Value] = MemberObject{
|
||||||
value: m.value,
|
Value: m.Value,
|
||||||
score: m.score,
|
Score: m.Score,
|
||||||
exists: true,
|
Exists: true,
|
||||||
}
|
}
|
||||||
count += 1
|
count += 1
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Policy not specified, just set the elements and scores
|
// Policy not specified, just Set the elements and scores
|
||||||
if set.members[m.value].score != m.score || !set.members[m.value].exists {
|
if set.members[m.Value].Score != m.Score || !set.members[m.Value].Exists {
|
||||||
count += 1
|
count += 1
|
||||||
}
|
}
|
||||||
set.members[m.value] = MemberObject{
|
set.members[m.Value] = MemberObject{
|
||||||
value: m.value,
|
Value: m.Value,
|
||||||
score: compareScores(set.members[m.value].score, m.score, comp),
|
Score: compareScores(set.members[m.Value].Score, m.Score, comp),
|
||||||
exists: true,
|
Exists: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count, nil
|
return count, nil
|
||||||
@@ -238,16 +238,16 @@ func (set *SortedSet) Pop(count int, policy string) (*SortedSet, error) {
|
|||||||
|
|
||||||
slices.SortFunc(members, func(a, b MemberParam) int {
|
slices.SortFunc(members, func(a, b MemberParam) int {
|
||||||
if strings.EqualFold(policy, "min") {
|
if strings.EqualFold(policy, "min") {
|
||||||
return cmp.Compare(a.score, b.score)
|
return cmp.Compare(a.Score, b.Score)
|
||||||
}
|
}
|
||||||
return cmp.Compare(b.score, a.score)
|
return cmp.Compare(b.Score, a.Score)
|
||||||
})
|
})
|
||||||
|
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
if i >= len(members) {
|
if i >= len(members) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
set.Remove(members[i].value)
|
set.Remove(members[i].Value)
|
||||||
_, err := popped.AddOrUpdate([]MemberParam{members[i]}, nil, nil, nil, nil)
|
_, err := popped.AddOrUpdate([]MemberParam{members[i]}, nil, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -261,8 +261,8 @@ func (set *SortedSet) Subtract(others []*SortedSet) *SortedSet {
|
|||||||
res := NewSortedSet(set.GetAll())
|
res := NewSortedSet(set.GetAll())
|
||||||
for _, ss := range others {
|
for _, ss := range others {
|
||||||
for _, m := range ss.GetAll() {
|
for _, m := range ss.GetAll() {
|
||||||
if res.Contains(m.value) {
|
if res.Contains(m.Value) {
|
||||||
res.Remove(m.value)
|
res.Remove(m.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -271,8 +271,8 @@ func (set *SortedSet) Subtract(others []*SortedSet) *SortedSet {
|
|||||||
|
|
||||||
// SortedSetParam is a composite object used for Intersect and Union function
|
// SortedSetParam is a composite object used for Intersect and Union function
|
||||||
type SortedSetParam struct {
|
type SortedSetParam struct {
|
||||||
set *SortedSet
|
Set *SortedSet
|
||||||
weight int
|
Weight int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (set *SortedSet) Equals(other *SortedSet) bool {
|
func (set *SortedSet) Equals(other *SortedSet) bool {
|
||||||
@@ -283,10 +283,10 @@ func (set *SortedSet) Equals(other *SortedSet) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
for _, member := range set.members {
|
for _, member := range set.members {
|
||||||
if !other.Contains(member.value) {
|
if !other.Contains(member.Value) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if member.score != other.Get(member.value).score {
|
if member.Score != other.Get(member.Value).Score {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -300,29 +300,29 @@ func Union(aggregate string, setParams ...SortedSetParam) *SortedSet {
|
|||||||
return NewSortedSet([]MemberParam{})
|
return NewSortedSet([]MemberParam{})
|
||||||
case 1:
|
case 1:
|
||||||
var params []MemberParam
|
var params []MemberParam
|
||||||
for _, member := range setParams[0].set.GetAll() {
|
for _, member := range setParams[0].Set.GetAll() {
|
||||||
params = append(params, MemberParam{
|
params = append(params, MemberParam{
|
||||||
value: member.value,
|
Value: member.Value,
|
||||||
score: member.score * Score(setParams[0].weight),
|
Score: member.Score * Score(setParams[0].Weight),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return NewSortedSet(params)
|
return NewSortedSet(params)
|
||||||
case 2:
|
case 2:
|
||||||
var params []MemberParam
|
var params []MemberParam
|
||||||
// Traverse the params in the left sorted set
|
// Traverse the params in the left sorted Set
|
||||||
for _, member := range setParams[0].set.GetAll() {
|
for _, member := range setParams[0].Set.GetAll() {
|
||||||
// If the member does not exist in the other sorted set, add it to params along with the appropriate weight
|
// If the member does not exist in the other sorted Set, add it to params along with the appropriate Weight
|
||||||
if !setParams[1].set.Contains(member.value) {
|
if !setParams[1].Set.Contains(member.Value) {
|
||||||
params = append(params, MemberParam{
|
params = append(params, MemberParam{
|
||||||
value: member.value,
|
Value: member.Value,
|
||||||
score: member.score * Score(setParams[0].weight),
|
Score: member.Score * Score(setParams[0].Weight),
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// If the member exists, get both elements and apply the weight
|
// If the member Exists, get both elements and apply the Weight
|
||||||
param := MemberParam{
|
param := MemberParam{
|
||||||
value: member.value,
|
Value: member.Value,
|
||||||
score: func(left, right Score) Score {
|
Score: func(left, right Score) Score {
|
||||||
// Choose which param to add to params depending on the aggregate
|
// Choose which param to add to params depending on the aggregate
|
||||||
switch aggregate {
|
switch aggregate {
|
||||||
case "sum":
|
case "sum":
|
||||||
@@ -334,21 +334,21 @@ func Union(aggregate string, setParams ...SortedSetParam) *SortedSet {
|
|||||||
return compareScores(left, right, "gt")
|
return compareScores(left, right, "gt")
|
||||||
}
|
}
|
||||||
}(
|
}(
|
||||||
member.score*Score(setParams[0].weight),
|
member.Score*Score(setParams[0].Weight),
|
||||||
setParams[1].set.Get(member.value).score*Score(setParams[1].weight),
|
setParams[1].Set.Get(member.Value).Score*Score(setParams[1].Weight),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
params = append(params, param)
|
params = append(params, param)
|
||||||
}
|
}
|
||||||
// Traverse the params on the right sorted set and add all the elements that are not
|
// Traverse the params on the right sorted Set and add all the elements that are not
|
||||||
// already contained in params with their respective weights applied.
|
// already contained in params with their respective weights applied.
|
||||||
for _, member := range setParams[1].set.GetAll() {
|
for _, member := range setParams[1].Set.GetAll() {
|
||||||
if !slices.ContainsFunc(params, func(param MemberParam) bool {
|
if !slices.ContainsFunc(params, func(param MemberParam) bool {
|
||||||
return param.value == member.value
|
return param.Value == member.Value
|
||||||
}) {
|
}) {
|
||||||
params = append(params, MemberParam{
|
params = append(params, MemberParam{
|
||||||
value: member.value,
|
Value: member.Value,
|
||||||
score: member.score * Score(setParams[1].weight),
|
Score: member.Score * Score(setParams[1].Weight),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -359,16 +359,16 @@ func Union(aggregate string, setParams ...SortedSetParam) *SortedSet {
|
|||||||
right := Union(aggregate, setParams[len(setParams)/2:]...)
|
right := Union(aggregate, setParams[len(setParams)/2:]...)
|
||||||
|
|
||||||
var params []MemberParam
|
var params []MemberParam
|
||||||
// Traverse left sub-set and add the union elements to params
|
// Traverse left sub-Set and add the union elements to params
|
||||||
for _, member := range left.GetAll() {
|
for _, member := range left.GetAll() {
|
||||||
if !right.Contains(member.value) {
|
if !right.Contains(member.Value) {
|
||||||
// If the right set does not contain the current element, just add it to params
|
// If the right Set does not contain the current element, just add it to params
|
||||||
params = append(params, member)
|
params = append(params, member)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
params = append(params, MemberParam{
|
params = append(params, MemberParam{
|
||||||
value: member.value,
|
Value: member.Value,
|
||||||
score: func(left, right Score) Score {
|
Score: func(left, right Score) Score {
|
||||||
switch aggregate {
|
switch aggregate {
|
||||||
case "sum":
|
case "sum":
|
||||||
return left + right
|
return left + right
|
||||||
@@ -378,13 +378,13 @@ func Union(aggregate string, setParams ...SortedSetParam) *SortedSet {
|
|||||||
// Aggregate is "max"
|
// Aggregate is "max"
|
||||||
return compareScores(left, right, "gt")
|
return compareScores(left, right, "gt")
|
||||||
}
|
}
|
||||||
}(member.score, right.Get(member.value).score),
|
}(member.Score, right.Get(member.Value).Score),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Traverse the right sub-set and add any remaining elements to params
|
// Traverse the right sub-Set and add any remaining elements to params
|
||||||
for _, member := range right.GetAll() {
|
for _, member := range right.GetAll() {
|
||||||
if !slices.ContainsFunc(params, func(param MemberParam) bool {
|
if !slices.ContainsFunc(params, func(param MemberParam) bool {
|
||||||
return param.value == member.value
|
return param.Value == member.Value
|
||||||
}) {
|
}) {
|
||||||
params = append(params, member)
|
params = append(params, member)
|
||||||
}
|
}
|
||||||
@@ -400,25 +400,25 @@ func Intersect(aggregate string, setParams ...SortedSetParam) *SortedSet {
|
|||||||
return NewSortedSet([]MemberParam{})
|
return NewSortedSet([]MemberParam{})
|
||||||
case 1:
|
case 1:
|
||||||
var params []MemberParam
|
var params []MemberParam
|
||||||
for _, member := range setParams[0].set.GetAll() {
|
for _, member := range setParams[0].Set.GetAll() {
|
||||||
params = append(params, MemberParam{
|
params = append(params, MemberParam{
|
||||||
value: member.value,
|
Value: member.Value,
|
||||||
score: member.score * Score(setParams[0].weight),
|
Score: member.Score * Score(setParams[0].Weight),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return NewSortedSet(params)
|
return NewSortedSet(params)
|
||||||
case 2:
|
case 2:
|
||||||
var params []MemberParam
|
var params []MemberParam
|
||||||
// Traverse the params in the left sorted set
|
// Traverse the params in the left sorted Set
|
||||||
for _, member := range setParams[0].set.GetAll() {
|
for _, member := range setParams[0].Set.GetAll() {
|
||||||
// Check if the member exists in the right sorted set
|
// Check if the member Exists in the right sorted Set
|
||||||
if !setParams[1].set.Contains(member.value) {
|
if !setParams[1].Set.Contains(member.Value) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// If the member exists, get both elements and apply the weight
|
// If the member Exists, get both elements and apply the Weight
|
||||||
param := MemberParam{
|
param := MemberParam{
|
||||||
value: member.value,
|
Value: member.Value,
|
||||||
score: func(left, right Score) Score {
|
Score: func(left, right Score) Score {
|
||||||
// Choose which param to add to params depending on the aggregate
|
// Choose which param to add to params depending on the aggregate
|
||||||
switch aggregate {
|
switch aggregate {
|
||||||
case "sum":
|
case "sum":
|
||||||
@@ -430,8 +430,8 @@ func Intersect(aggregate string, setParams ...SortedSetParam) *SortedSet {
|
|||||||
return compareScores(left, right, "gt")
|
return compareScores(left, right, "gt")
|
||||||
}
|
}
|
||||||
}(
|
}(
|
||||||
member.score*Score(setParams[0].weight),
|
member.Score*Score(setParams[0].Weight),
|
||||||
setParams[1].set.Get(member.value).score*Score(setParams[1].weight),
|
setParams[1].Set.Get(member.Value).Score*Score(setParams[1].Weight),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
params = append(params, param)
|
params = append(params, param)
|
||||||
@@ -444,12 +444,12 @@ func Intersect(aggregate string, setParams ...SortedSetParam) *SortedSet {
|
|||||||
|
|
||||||
var params []MemberParam
|
var params []MemberParam
|
||||||
for _, member := range left.GetAll() {
|
for _, member := range left.GetAll() {
|
||||||
if !right.Contains(member.value) {
|
if !right.Contains(member.Value) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
params = append(params, MemberParam{
|
params = append(params, MemberParam{
|
||||||
value: member.value,
|
Value: member.Value,
|
||||||
score: func(left, right Score) Score {
|
Score: func(left, right Score) Score {
|
||||||
switch aggregate {
|
switch aggregate {
|
||||||
case "sum":
|
case "sum":
|
||||||
return left + right
|
return left + right
|
||||||
@@ -459,7 +459,7 @@ func Intersect(aggregate string, setParams ...SortedSetParam) *SortedSet {
|
|||||||
// Aggregate is "max"
|
// Aggregate is "max"
|
||||||
return compareScores(left, right, "gt")
|
return compareScores(left, right, "gt")
|
||||||
}
|
}
|
||||||
}(member.score, right.Get(member.value).score),
|
}(member.Score, right.Get(member.Value).Score),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
98
internal/sorted_set/utils.go
Normal file
98
internal/sorted_set/utils.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
// 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 sorted_set
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func validateUpdatePolicy(updatePolicy interface{}) (string, error) {
|
||||||
|
if updatePolicy == nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
err := errors.New("update policy must be a string of Value NX or XX")
|
||||||
|
policy, ok := updatePolicy.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !slices.Contains([]string{"nx", "xx"}, strings.ToLower(policy)) {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return policy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateComparison(comparison interface{}) (string, error) {
|
||||||
|
if comparison == nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
err := errors.New("comparison condition must be a string of Value LT or GT")
|
||||||
|
comp, ok := comparison.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !slices.Contains([]string{"lt", "gt"}, strings.ToLower(comp)) {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return comp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateChanged(changed interface{}) (string, error) {
|
||||||
|
if changed == nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
err := errors.New("changed condition should be a string of Value CH")
|
||||||
|
ch, ok := changed.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !strings.EqualFold(ch, "ch") {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return ch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateIncr(incr interface{}) (string, error) {
|
||||||
|
if incr == nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
err := errors.New("incr condition should be a string of Value INCR")
|
||||||
|
i, ok := incr.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !strings.EqualFold(i, "incr") {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareScores(old Score, new Score, comp string) Score {
|
||||||
|
switch strings.ToLower(comp) {
|
||||||
|
default:
|
||||||
|
return new
|
||||||
|
case "lt":
|
||||||
|
if new < old {
|
||||||
|
return new
|
||||||
|
}
|
||||||
|
return old
|
||||||
|
case "gt":
|
||||||
|
if new > old {
|
||||||
|
return new
|
||||||
|
}
|
||||||
|
return old
|
||||||
|
}
|
||||||
|
}
|
@@ -17,6 +17,7 @@ package internal
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"cmp"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/echovault/echovault/pkg/utils"
|
"github.com/echovault/echovault/pkg/utils"
|
||||||
@@ -24,6 +25,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -220,6 +222,35 @@ func FilterExpiredKeys(state map[string]KeyData) map[string]KeyData {
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CompareLex returns -1 when s2 is lexicographically greater than s1,
|
||||||
|
// 0 if they're equal and 1 if s2 is lexicographically less than s1.
|
||||||
|
func CompareLex(s1 string, s2 string) int {
|
||||||
|
if s1 == s2 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if strings.Contains(s1, s2) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if strings.Contains(s2, s1) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
limit := len(s1)
|
||||||
|
if len(s2) < limit {
|
||||||
|
limit = len(s2)
|
||||||
|
}
|
||||||
|
|
||||||
|
var c int
|
||||||
|
for i := 0; i < limit; i++ {
|
||||||
|
c = cmp.Compare(s1[i], s2[i])
|
||||||
|
if c != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
func EncodeCommand(cmd []string) []byte {
|
func EncodeCommand(cmd []string) []byte {
|
||||||
res := fmt.Sprintf("*%d\r\n", len(cmd))
|
res := fmt.Sprintf("*%d\r\n", len(cmd))
|
||||||
for _, token := range cmd {
|
for _, token := range cmd {
|
||||||
@@ -356,3 +387,21 @@ func ParseBooleanArrayResponse(b []byte) ([]bool, error) {
|
|||||||
}
|
}
|
||||||
return arr, nil
|
return arr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CompareNestedStringArrays(got [][]string, want [][]string) bool {
|
||||||
|
for _, wantItem := range want {
|
||||||
|
if !slices.ContainsFunc(got, func(gotItem []string) bool {
|
||||||
|
return reflect.DeepEqual(wantItem, gotItem)
|
||||||
|
}) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, gotItem := range got {
|
||||||
|
if !slices.ContainsFunc(want, func(wantItem []string) bool {
|
||||||
|
return reflect.DeepEqual(wantItem, gotItem)
|
||||||
|
}) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
140686
pkg/echovault/aof/log.aof
140686
pkg/echovault/aof/log.aof
File diff suppressed because it is too large
Load Diff
@@ -7,12 +7,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func presetValue(server *EchoVault, key string, value interface{}) {
|
|
||||||
_, _ = server.CreateKeyAndLock(server.context, key)
|
|
||||||
_ = server.SetValue(server.context, key, value)
|
|
||||||
server.KeyUnlock(server.context, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEchoVault_LLEN(t *testing.T) {
|
func TestEchoVault_LLEN(t *testing.T) {
|
||||||
server := NewEchoVault(WithCommands(commands.All()))
|
server := NewEchoVault(WithCommands(commands.All()))
|
||||||
|
|
||||||
|
@@ -40,15 +40,15 @@ type ZUNIONSTOREOptions ZINTEROptions
|
|||||||
type ZMPOPOptions struct {
|
type ZMPOPOptions struct {
|
||||||
Min bool
|
Min bool
|
||||||
Max bool
|
Max bool
|
||||||
Count int
|
Count uint
|
||||||
}
|
}
|
||||||
|
|
||||||
type ZRANGEOptions struct {
|
type ZRANGEOptions struct {
|
||||||
ByScore bool
|
WithScores bool
|
||||||
ByLex bool
|
ByScore bool
|
||||||
Rev bool
|
ByLex bool
|
||||||
Offset int
|
Offset uint
|
||||||
Count int
|
Count uint
|
||||||
}
|
}
|
||||||
type ZRANGESTOREOptions ZRANGEOptions
|
type ZRANGESTOREOptions ZRANGEOptions
|
||||||
|
|
||||||
@@ -87,8 +87,8 @@ func buildIntegerScoreMap(arr [][]string, withscores bool) (map[int]float64, err
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *EchoVault) ZADD(entries map[string]float64, options ZADDOptions) (int, error) {
|
func (server *EchoVault) ZADD(key string, entries map[string]float64, options ZADDOptions) (int, error) {
|
||||||
cmd := []string{"ZADD"}
|
cmd := []string{"ZADD", key}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case options.NX:
|
case options.NX:
|
||||||
@@ -113,7 +113,7 @@ func (server *EchoVault) ZADD(entries map[string]float64, options ZADDOptions) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
for member, score := range entries {
|
for member, score := range entries {
|
||||||
cmd = append(cmd, []string{member, strconv.FormatFloat(score, 'f', -1, 64)}...)
|
cmd = append(cmd, []string{strconv.FormatFloat(score, 'f', -1, 64), member}...)
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false)
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false)
|
||||||
@@ -191,8 +191,8 @@ func (server *EchoVault) ZINTER(keys []string, options ZINTEROptions) (map[strin
|
|||||||
|
|
||||||
if len(options.Weights) > 0 {
|
if len(options.Weights) > 0 {
|
||||||
cmd = append(cmd, "WEIGHTS")
|
cmd = append(cmd, "WEIGHTS")
|
||||||
for _, weight := range options.Weights {
|
for i := 0; i < len(options.Weights); i++ {
|
||||||
cmd = append(cmd, strconv.FormatFloat(float64(weight), 'f', -1, 64))
|
cmd = append(cmd, strconv.FormatFloat(options.Weights[i], 'f', -1, 64))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,7 +314,7 @@ func (server *EchoVault) ZMPOP(keys []string, options ZMPOPOptions) ([][]string,
|
|||||||
|
|
||||||
switch {
|
switch {
|
||||||
case options.Count != 0:
|
case options.Count != 0:
|
||||||
cmd = append(cmd, []string{"COUNT", strconv.Itoa(options.Count)}...)
|
cmd = append(cmd, []string{"COUNT", strconv.Itoa(int(options.Count))}...)
|
||||||
default:
|
default:
|
||||||
cmd = append(cmd, []string{"COUNT", strconv.Itoa(1)}...)
|
cmd = append(cmd, []string{"COUNT", strconv.Itoa(1)}...)
|
||||||
}
|
}
|
||||||
@@ -327,7 +327,7 @@ func (server *EchoVault) ZMPOP(keys []string, options ZMPOPOptions) ([][]string,
|
|||||||
return internal.ParseNestedStringArrayResponse(b)
|
return internal.ParseNestedStringArrayResponse(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *EchoVault) ZMSCORE(key string, members ...string) ([]float64, error) {
|
func (server *EchoVault) ZMSCORE(key string, members ...string) ([]interface{}, error) {
|
||||||
cmd := []string{"ZMSCORE", key}
|
cmd := []string{"ZMSCORE", key}
|
||||||
for _, member := range members {
|
for _, member := range members {
|
||||||
cmd = append(cmd, member)
|
cmd = append(cmd, member)
|
||||||
@@ -343,8 +343,12 @@ func (server *EchoVault) ZMSCORE(key string, members ...string) ([]float64, erro
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
scores := make([]float64, len(arr))
|
scores := make([]interface{}, len(arr))
|
||||||
for i, e := range arr {
|
for i, e := range arr {
|
||||||
|
if e == "" {
|
||||||
|
scores[i] = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
score, err := strconv.ParseFloat(e, 64)
|
score, err := strconv.ParseFloat(e, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -381,7 +385,7 @@ func (server *EchoVault) ZPOPMIN(key string, count int) ([][]string, error) {
|
|||||||
return internal.ParseNestedStringArrayResponse(b)
|
return internal.ParseNestedStringArrayResponse(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *EchoVault) ZRANDMEMBER(key string, count int, withscores bool) (map[string]float64, error) {
|
func (server *EchoVault) ZRANDMEMBER(key string, count int, withscores bool) ([][]string, error) {
|
||||||
cmd := []string{"ZRANDMEMBER", key}
|
cmd := []string{"ZRANDMEMBER", key}
|
||||||
if count != 0 {
|
if count != 0 {
|
||||||
cmd = append(cmd, strconv.Itoa(count))
|
cmd = append(cmd, strconv.Itoa(count))
|
||||||
@@ -395,12 +399,7 @@ func (server *EchoVault) ZRANDMEMBER(key string, count int, withscores bool) (ma
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
arr, err := internal.ParseNestedStringArrayResponse(b)
|
return internal.ParseNestedStringArrayResponse(b)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return buildMemberScoreMap(arr, withscores)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *EchoVault) ZRANK(key string, member string, withscores bool) (map[int]float64, error) {
|
func (server *EchoVault) ZRANK(key string, member string, withscores bool) (map[int]float64, error) {
|
||||||
@@ -414,9 +413,28 @@ func (server *EchoVault) ZRANK(key string, member string, withscores bool) (map[
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
arr, err := internal.ParseNestedStringArrayResponse(b)
|
arr, err := internal.ParseStringArrayResponse(b)
|
||||||
|
|
||||||
return buildIntegerScoreMap(arr, withscores)
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *EchoVault) ZREVRANK(key string, member string, withscores bool) (map[int]float64, error) {
|
func (server *EchoVault) ZREVRANK(key string, member string, withscores bool) (map[int]float64, error) {
|
||||||
@@ -508,11 +526,13 @@ func (server *EchoVault) ZRANGE(key, start, stop string, options ZRANGEOptions)
|
|||||||
cmd = append(cmd, "BYSCORE")
|
cmd = append(cmd, "BYSCORE")
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.Rev {
|
if options.WithScores {
|
||||||
cmd = append(cmd, "REV")
|
cmd = append(cmd, "WITHSCORES")
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = append(cmd, []string{"LIMIT", strconv.Itoa(options.Offset), strconv.Itoa(options.Count)}...)
|
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)
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -524,7 +544,7 @@ func (server *EchoVault) ZRANGE(key, start, stop string, options ZRANGEOptions)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildMemberScoreMap(arr, true)
|
return buildMemberScoreMap(arr, options.WithScores)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *EchoVault) ZRANGESTORE(destination, source, start, stop string, options ZRANGESTOREOptions) (int, error) {
|
func (server *EchoVault) ZRANGESTORE(destination, source, start, stop string, options ZRANGESTOREOptions) (int, error) {
|
||||||
@@ -539,12 +559,10 @@ func (server *EchoVault) ZRANGESTORE(destination, source, start, stop string, op
|
|||||||
cmd = append(cmd, "BYSCORE")
|
cmd = append(cmd, "BYSCORE")
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.Rev {
|
if options.Offset != 0 && options.Count != 0 {
|
||||||
cmd = append(cmd, "REV")
|
cmd = append(cmd, []string{"LIMIT", strconv.Itoa(int(options.Offset)), strconv.Itoa(int(options.Count))}...)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = append(cmd, []string{"LIMIT", strconv.Itoa(options.Offset), strconv.Itoa(options.Count)}...)
|
|
||||||
|
|
||||||
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false)
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
3203
pkg/echovault/api_sorted_set_test.go
Normal file
3203
pkg/echovault/api_sorted_set_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -565,3 +565,9 @@ func (server *EchoVault) evictKeysWithExpiredTTL(ctx context.Context) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func presetValue(server *EchoVault, key string, value interface{}) {
|
||||||
|
_, _ = server.CreateKeyAndLock(server.context, key)
|
||||||
|
_ = server.SetValue(server.context, key, value)
|
||||||
|
server.KeyUnlock(server.context, key)
|
||||||
|
}
|
||||||
|
@@ -20,6 +20,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/echovault/echovault/internal"
|
"github.com/echovault/echovault/internal"
|
||||||
|
"github.com/echovault/echovault/internal/sorted_set"
|
||||||
"github.com/echovault/echovault/pkg/utils"
|
"github.com/echovault/echovault/pkg/utils"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
@@ -63,7 +64,7 @@ func handleZADD(ctx context.Context, cmd []string, server utils.EchoVault, conn
|
|||||||
return nil, errors.New("score/member pairs must be float/string")
|
return nil, errors.New("score/member pairs must be float/string")
|
||||||
}
|
}
|
||||||
|
|
||||||
var members []MemberParam
|
var members []sorted_set.MemberParam
|
||||||
|
|
||||||
for i := 0; i < len(cmd[membersStartIndex:]); i++ {
|
for i := 0; i < len(cmd[membersStartIndex:]); i++ {
|
||||||
if i%2 != 0 {
|
if i%2 != 0 {
|
||||||
@@ -77,29 +78,29 @@ func handleZADD(ctx context.Context, cmd []string, server utils.EchoVault, conn
|
|||||||
var s float64
|
var s float64
|
||||||
if strings.ToLower(score.(string)) == "-inf" {
|
if strings.ToLower(score.(string)) == "-inf" {
|
||||||
s = math.Inf(-1)
|
s = math.Inf(-1)
|
||||||
members = append(members, MemberParam{
|
members = append(members, sorted_set.MemberParam{
|
||||||
value: Value(cmd[membersStartIndex:][i+1]),
|
Value: sorted_set.Value(cmd[membersStartIndex:][i+1]),
|
||||||
score: Score(s),
|
Score: sorted_set.Score(s),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if strings.ToLower(score.(string)) == "+inf" {
|
if strings.ToLower(score.(string)) == "+inf" {
|
||||||
s = math.Inf(1)
|
s = math.Inf(1)
|
||||||
members = append(members, MemberParam{
|
members = append(members, sorted_set.MemberParam{
|
||||||
value: Value(cmd[membersStartIndex:][i+1]),
|
Value: sorted_set.Value(cmd[membersStartIndex:][i+1]),
|
||||||
score: Score(s),
|
Score: sorted_set.Score(s),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
case float64:
|
case float64:
|
||||||
s, _ := score.(float64)
|
s, _ := score.(float64)
|
||||||
members = append(members, MemberParam{
|
members = append(members, sorted_set.MemberParam{
|
||||||
value: Value(cmd[membersStartIndex:][i+1]),
|
Value: sorted_set.Value(cmd[membersStartIndex:][i+1]),
|
||||||
score: Score(s),
|
Score: sorted_set.Score(s),
|
||||||
})
|
})
|
||||||
case int:
|
case int:
|
||||||
s, _ := score.(int)
|
s, _ := score.(int)
|
||||||
members = append(members, MemberParam{
|
members = append(members, sorted_set.MemberParam{
|
||||||
value: Value(cmd[membersStartIndex:][i+1]),
|
Value: sorted_set.Value(cmd[membersStartIndex:][i+1]),
|
||||||
score: Score(s),
|
Score: sorted_set.Score(s),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,7 +149,7 @@ func handleZADD(ctx context.Context, cmd []string, server utils.EchoVault, conn
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer server.KeyUnlock(ctx, key)
|
defer server.KeyUnlock(ctx, key)
|
||||||
set, ok := server.GetValue(ctx, key).(*SortedSet)
|
set, ok := server.GetValue(ctx, key).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
||||||
}
|
}
|
||||||
@@ -158,8 +159,8 @@ func handleZADD(ctx context.Context, cmd []string, server utils.EchoVault, conn
|
|||||||
}
|
}
|
||||||
// If INCR option is provided, return the new score value
|
// If INCR option is provided, return the new score value
|
||||||
if incr != nil {
|
if incr != nil {
|
||||||
m := set.Get(members[0].value)
|
m := set.Get(members[0].Value)
|
||||||
return []byte(fmt.Sprintf("+%f\r\n", m.score)), nil
|
return []byte(fmt.Sprintf("+%f\r\n", m.Score)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return []byte(fmt.Sprintf(":%d\r\n", count)), nil
|
return []byte(fmt.Sprintf(":%d\r\n", count)), nil
|
||||||
@@ -171,7 +172,7 @@ func handleZADD(ctx context.Context, cmd []string, server utils.EchoVault, conn
|
|||||||
}
|
}
|
||||||
defer server.KeyUnlock(ctx, key)
|
defer server.KeyUnlock(ctx, key)
|
||||||
|
|
||||||
set := NewSortedSet(members)
|
set := sorted_set.NewSortedSet(members)
|
||||||
if err = server.SetValue(ctx, key, set); err != nil {
|
if err = server.SetValue(ctx, key, set); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -195,7 +196,7 @@ func handleZCARD(ctx context.Context, cmd []string, server utils.EchoVault, conn
|
|||||||
}
|
}
|
||||||
defer server.KeyRUnlock(ctx, key)
|
defer server.KeyRUnlock(ctx, key)
|
||||||
|
|
||||||
set, ok := server.GetValue(ctx, key).(*SortedSet)
|
set, ok := server.GetValue(ctx, key).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
||||||
}
|
}
|
||||||
@@ -211,40 +212,40 @@ func handleZCOUNT(ctx context.Context, cmd []string, server utils.EchoVault, con
|
|||||||
|
|
||||||
key := keys[0]
|
key := keys[0]
|
||||||
|
|
||||||
minimum := Score(math.Inf(-1))
|
minimum := sorted_set.Score(math.Inf(-1))
|
||||||
switch internal.AdaptType(cmd[2]).(type) {
|
switch internal.AdaptType(cmd[2]).(type) {
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("min constraint must be a double")
|
return nil, errors.New("min constraint must be a double")
|
||||||
case string:
|
case string:
|
||||||
if strings.ToLower(cmd[2]) == "+inf" {
|
if strings.ToLower(cmd[2]) == "+inf" {
|
||||||
minimum = Score(math.Inf(1))
|
minimum = sorted_set.Score(math.Inf(1))
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("min constraint must be a double")
|
return nil, errors.New("min constraint must be a double")
|
||||||
}
|
}
|
||||||
case float64:
|
case float64:
|
||||||
s, _ := internal.AdaptType(cmd[2]).(float64)
|
s, _ := internal.AdaptType(cmd[2]).(float64)
|
||||||
minimum = Score(s)
|
minimum = sorted_set.Score(s)
|
||||||
case int:
|
case int:
|
||||||
s, _ := internal.AdaptType(cmd[2]).(int)
|
s, _ := internal.AdaptType(cmd[2]).(int)
|
||||||
minimum = Score(s)
|
minimum = sorted_set.Score(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
maximum := Score(math.Inf(1))
|
maximum := sorted_set.Score(math.Inf(1))
|
||||||
switch internal.AdaptType(cmd[3]).(type) {
|
switch internal.AdaptType(cmd[3]).(type) {
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("max constraint must be a double")
|
return nil, errors.New("max constraint must be a double")
|
||||||
case string:
|
case string:
|
||||||
if strings.ToLower(cmd[3]) == "-inf" {
|
if strings.ToLower(cmd[3]) == "-inf" {
|
||||||
maximum = Score(math.Inf(-1))
|
maximum = sorted_set.Score(math.Inf(-1))
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("max constraint must be a double")
|
return nil, errors.New("max constraint must be a double")
|
||||||
}
|
}
|
||||||
case float64:
|
case float64:
|
||||||
s, _ := internal.AdaptType(cmd[3]).(float64)
|
s, _ := internal.AdaptType(cmd[3]).(float64)
|
||||||
maximum = Score(s)
|
maximum = sorted_set.Score(s)
|
||||||
case int:
|
case int:
|
||||||
s, _ := internal.AdaptType(cmd[3]).(int)
|
s, _ := internal.AdaptType(cmd[3]).(int)
|
||||||
maximum = Score(s)
|
maximum = sorted_set.Score(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !server.KeyExists(ctx, key) {
|
if !server.KeyExists(ctx, key) {
|
||||||
@@ -256,14 +257,14 @@ func handleZCOUNT(ctx context.Context, cmd []string, server utils.EchoVault, con
|
|||||||
}
|
}
|
||||||
defer server.KeyRUnlock(ctx, key)
|
defer server.KeyRUnlock(ctx, key)
|
||||||
|
|
||||||
set, ok := server.GetValue(ctx, key).(*SortedSet)
|
set, ok := server.GetValue(ctx, key).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
var members []MemberParam
|
var members []sorted_set.MemberParam
|
||||||
for _, m := range set.GetAll() {
|
for _, m := range set.GetAll() {
|
||||||
if m.score >= minimum && m.score <= maximum {
|
if m.Score >= minimum && m.Score <= maximum {
|
||||||
members = append(members, m)
|
members = append(members, m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -271,7 +272,7 @@ func handleZCOUNT(ctx context.Context, cmd []string, server utils.EchoVault, con
|
|||||||
return []byte(fmt.Sprintf(":%d\r\n", len(members))), nil
|
return []byte(fmt.Sprintf(":%d\r\n", len(members))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleZLEXCOUNT(ctx context.Context, cmd []string, server utils.EchoVault, conn *net.Conn) ([]byte, error) {
|
func handleZLEXCOUNT(ctx context.Context, cmd []string, server utils.EchoVault, _ *net.Conn) ([]byte, error) {
|
||||||
keys, err := zlexcountKeyFunc(cmd)
|
keys, err := zlexcountKeyFunc(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -290,7 +291,7 @@ func handleZLEXCOUNT(ctx context.Context, cmd []string, server utils.EchoVault,
|
|||||||
}
|
}
|
||||||
defer server.KeyRUnlock(ctx, key)
|
defer server.KeyRUnlock(ctx, key)
|
||||||
|
|
||||||
set, ok := server.GetValue(ctx, key).(*SortedSet)
|
set, ok := server.GetValue(ctx, key).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
||||||
}
|
}
|
||||||
@@ -299,7 +300,7 @@ func handleZLEXCOUNT(ctx context.Context, cmd []string, server utils.EchoVault,
|
|||||||
|
|
||||||
// Check if all members has the same score
|
// Check if all members has the same score
|
||||||
for i := 0; i < len(members)-2; i++ {
|
for i := 0; i < len(members)-2; i++ {
|
||||||
if members[i].score != members[i+1].score {
|
if members[i].Score != members[i+1].Score {
|
||||||
return []byte(":0\r\n"), nil
|
return []byte(":0\r\n"), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,8 +308,8 @@ func handleZLEXCOUNT(ctx context.Context, cmd []string, server utils.EchoVault,
|
|||||||
count := 0
|
count := 0
|
||||||
|
|
||||||
for _, m := range members {
|
for _, m := range members {
|
||||||
if slices.Contains([]int{1, 0}, compareLex(string(m.value), minimum)) &&
|
if slices.Contains([]int{1, 0}, internal.CompareLex(string(m.Value), minimum)) &&
|
||||||
slices.Contains([]int{-1, 0}, compareLex(string(m.value), maximum)) {
|
slices.Contains([]int{-1, 0}, internal.CompareLex(string(m.Value), maximum)) {
|
||||||
count += 1
|
count += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,13 +348,13 @@ func handleZDIFF(ctx context.Context, cmd []string, server utils.EchoVault, conn
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer server.KeyRUnlock(ctx, keys[0])
|
defer server.KeyRUnlock(ctx, keys[0])
|
||||||
baseSortedSet, ok := server.GetValue(ctx, keys[0]).(*SortedSet)
|
baseSortedSet, ok := server.GetValue(ctx, keys[0]).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", keys[0])
|
return nil, fmt.Errorf("value at %s is not a sorted set", keys[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the remaining sets
|
// Extract the remaining sets
|
||||||
var sets []*SortedSet
|
var sets []*sorted_set.SortedSet
|
||||||
|
|
||||||
for i := 1; i < len(keys); i++ {
|
for i := 1; i < len(keys); i++ {
|
||||||
if !server.KeyExists(ctx, keys[i]) {
|
if !server.KeyExists(ctx, keys[i]) {
|
||||||
@@ -364,7 +365,7 @@ func handleZDIFF(ctx context.Context, cmd []string, server utils.EchoVault, conn
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
locks[keys[i]] = locked
|
locks[keys[i]] = locked
|
||||||
set, ok := server.GetValue(ctx, keys[i]).(*SortedSet)
|
set, ok := server.GetValue(ctx, keys[i]).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", keys[i])
|
return nil, fmt.Errorf("value at %s is not a sorted set", keys[i])
|
||||||
}
|
}
|
||||||
@@ -378,9 +379,9 @@ func handleZDIFF(ctx context.Context, cmd []string, server utils.EchoVault, conn
|
|||||||
|
|
||||||
for _, m := range diff.GetAll() {
|
for _, m := range diff.GetAll() {
|
||||||
if includeScores {
|
if includeScores {
|
||||||
res += fmt.Sprintf("\r\n*2\r\n$%d\r\n%s\r\n+%s", len(m.value), m.value, strconv.FormatFloat(float64(m.score), 'f', -1, 64))
|
res += fmt.Sprintf("\r\n*2\r\n$%d\r\n%s\r\n+%s", len(m.Value), m.Value, strconv.FormatFloat(float64(m.Score), 'f', -1, 64))
|
||||||
} else {
|
} else {
|
||||||
res += fmt.Sprintf("\r\n*1\r\n$%d\r\n%s", len(m.value), m.value)
|
res += fmt.Sprintf("\r\n*1\r\n$%d\r\n%s", len(m.Value), m.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,19 +416,19 @@ func handleZDIFFSTORE(ctx context.Context, cmd []string, server utils.EchoVault,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer server.KeyRUnlock(ctx, keys[0])
|
defer server.KeyRUnlock(ctx, keys[0])
|
||||||
baseSortedSet, ok := server.GetValue(ctx, keys[0]).(*SortedSet)
|
baseSortedSet, ok := server.GetValue(ctx, keys[0]).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", keys[0])
|
return nil, fmt.Errorf("value at %s is not a sorted set", keys[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
var sets []*SortedSet
|
var sets []*sorted_set.SortedSet
|
||||||
|
|
||||||
for i := 1; i < len(keys); i++ {
|
for i := 1; i < len(keys); i++ {
|
||||||
if server.KeyExists(ctx, keys[i]) {
|
if server.KeyExists(ctx, keys[i]) {
|
||||||
if _, err = server.KeyRLock(ctx, keys[i]); err != nil {
|
if _, err = server.KeyRLock(ctx, keys[i]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
set, ok := server.GetValue(ctx, keys[i]).(*SortedSet)
|
set, ok := server.GetValue(ctx, keys[i]).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", keys[i])
|
return nil, fmt.Errorf("value at %s is not a sorted set", keys[i])
|
||||||
}
|
}
|
||||||
@@ -462,26 +463,26 @@ func handleZINCRBY(ctx context.Context, cmd []string, server utils.EchoVault, co
|
|||||||
}
|
}
|
||||||
|
|
||||||
key := keys[0]
|
key := keys[0]
|
||||||
member := Value(cmd[3])
|
member := sorted_set.Value(cmd[3])
|
||||||
var increment Score
|
var increment sorted_set.Score
|
||||||
|
|
||||||
switch internal.AdaptType(cmd[2]).(type) {
|
switch internal.AdaptType(cmd[2]).(type) {
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("increment must be a double")
|
return nil, errors.New("increment must be a double")
|
||||||
case string:
|
case string:
|
||||||
if strings.EqualFold("-inf", strings.ToLower(cmd[2])) {
|
if strings.EqualFold("-inf", strings.ToLower(cmd[2])) {
|
||||||
increment = Score(math.Inf(-1))
|
increment = sorted_set.Score(math.Inf(-1))
|
||||||
} else if strings.EqualFold("+inf", strings.ToLower(cmd[2])) {
|
} else if strings.EqualFold("+inf", strings.ToLower(cmd[2])) {
|
||||||
increment = Score(math.Inf(1))
|
increment = sorted_set.Score(math.Inf(1))
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("increment must be a double")
|
return nil, errors.New("increment must be a double")
|
||||||
}
|
}
|
||||||
case float64:
|
case float64:
|
||||||
s, _ := internal.AdaptType(cmd[2]).(float64)
|
s, _ := internal.AdaptType(cmd[2]).(float64)
|
||||||
increment = Score(s)
|
increment = sorted_set.Score(s)
|
||||||
case int:
|
case int:
|
||||||
s, _ := internal.AdaptType(cmd[2]).(int)
|
s, _ := internal.AdaptType(cmd[2]).(int)
|
||||||
increment = Score(s)
|
increment = sorted_set.Score(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !server.KeyExists(ctx, key) {
|
if !server.KeyExists(ctx, key) {
|
||||||
@@ -490,7 +491,11 @@ func handleZINCRBY(ctx context.Context, cmd []string, server utils.EchoVault, co
|
|||||||
if _, err = server.CreateKeyAndLock(ctx, key); err != nil {
|
if _, err = server.CreateKeyAndLock(ctx, key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err = server.SetValue(ctx, key, NewSortedSet([]MemberParam{{value: member, score: increment}})); err != nil {
|
if err = server.SetValue(
|
||||||
|
ctx,
|
||||||
|
key,
|
||||||
|
sorted_set.NewSortedSet([]sorted_set.MemberParam{{Value: member, Score: increment}}),
|
||||||
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
server.KeyUnlock(ctx, key)
|
server.KeyUnlock(ctx, key)
|
||||||
@@ -501,13 +506,13 @@ func handleZINCRBY(ctx context.Context, cmd []string, server utils.EchoVault, co
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer server.KeyUnlock(ctx, key)
|
defer server.KeyUnlock(ctx, key)
|
||||||
set, ok := server.GetValue(ctx, key).(*SortedSet)
|
set, ok := server.GetValue(ctx, key).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
||||||
}
|
}
|
||||||
if _, err = set.AddOrUpdate(
|
if _, err = set.AddOrUpdate(
|
||||||
[]MemberParam{
|
[]sorted_set.MemberParam{
|
||||||
{value: member, score: increment}},
|
{Value: member, Score: increment}},
|
||||||
"xx",
|
"xx",
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
@@ -515,7 +520,7 @@ func handleZINCRBY(ctx context.Context, cmd []string, server utils.EchoVault, co
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return []byte(fmt.Sprintf("+%s\r\n",
|
return []byte(fmt.Sprintf("+%s\r\n",
|
||||||
strconv.FormatFloat(float64(set.Get(member).score), 'f', -1, 64))), nil
|
strconv.FormatFloat(float64(set.Get(member).Score), 'f', -1, 64))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleZINTER(ctx context.Context, cmd []string, server utils.EchoVault, conn *net.Conn) ([]byte, error) {
|
func handleZINTER(ctx context.Context, cmd []string, server utils.EchoVault, conn *net.Conn) ([]byte, error) {
|
||||||
@@ -538,7 +543,7 @@ func handleZINTER(ctx context.Context, cmd []string, server utils.EchoVault, con
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var setParams []SortedSetParam
|
var setParams []sorted_set.SortedSetParam
|
||||||
|
|
||||||
for i := 0; i < len(keys); i++ {
|
for i := 0; i < len(keys); i++ {
|
||||||
if !server.KeyExists(ctx, keys[i]) {
|
if !server.KeyExists(ctx, keys[i]) {
|
||||||
@@ -549,26 +554,26 @@ func handleZINTER(ctx context.Context, cmd []string, server utils.EchoVault, con
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
locks[keys[i]] = true
|
locks[keys[i]] = true
|
||||||
set, ok := server.GetValue(ctx, keys[i]).(*SortedSet)
|
set, ok := server.GetValue(ctx, keys[i]).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", keys[i])
|
return nil, fmt.Errorf("value at %s is not a sorted set", keys[i])
|
||||||
}
|
}
|
||||||
setParams = append(setParams, SortedSetParam{
|
setParams = append(setParams, sorted_set.SortedSetParam{
|
||||||
set: set,
|
Set: set,
|
||||||
weight: weights[i],
|
Weight: weights[i],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
intersect := Intersect(aggregate, setParams...)
|
intersect := sorted_set.Intersect(aggregate, setParams...)
|
||||||
|
|
||||||
res := fmt.Sprintf("*%d", intersect.Cardinality())
|
res := fmt.Sprintf("*%d", intersect.Cardinality())
|
||||||
|
|
||||||
if intersect.Cardinality() > 0 {
|
if intersect.Cardinality() > 0 {
|
||||||
for _, m := range intersect.GetAll() {
|
for _, m := range intersect.GetAll() {
|
||||||
if withscores {
|
if withscores {
|
||||||
res += fmt.Sprintf("\r\n*2\r\n$%d\r\n%s\r\n+%s", len(m.value), m.value, strconv.FormatFloat(float64(m.score), 'f', -1, 64))
|
res += fmt.Sprintf("\r\n*2\r\n$%d\r\n%s\r\n+%s", len(m.Value), m.Value, strconv.FormatFloat(float64(m.Score), 'f', -1, 64))
|
||||||
} else {
|
} else {
|
||||||
res += fmt.Sprintf("\r\n*1\r\n$%d\r\n%s", len(m.value), m.value)
|
res += fmt.Sprintf("\r\n*1\r\n$%d\r\n%s", len(m.Value), m.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -605,7 +610,7 @@ func handleZINTERSTORE(ctx context.Context, cmd []string, server utils.EchoVault
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var setParams []SortedSetParam
|
var setParams []sorted_set.SortedSetParam
|
||||||
|
|
||||||
for i := 0; i < len(keys); i++ {
|
for i := 0; i < len(keys); i++ {
|
||||||
if !server.KeyExists(ctx, keys[i]) {
|
if !server.KeyExists(ctx, keys[i]) {
|
||||||
@@ -615,17 +620,17 @@ func handleZINTERSTORE(ctx context.Context, cmd []string, server utils.EchoVault
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
locks[keys[i]] = true
|
locks[keys[i]] = true
|
||||||
set, ok := server.GetValue(ctx, keys[i]).(*SortedSet)
|
set, ok := server.GetValue(ctx, keys[i]).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", keys[i])
|
return nil, fmt.Errorf("value at %s is not a sorted set", keys[i])
|
||||||
}
|
}
|
||||||
setParams = append(setParams, SortedSetParam{
|
setParams = append(setParams, sorted_set.SortedSetParam{
|
||||||
set: set,
|
Set: set,
|
||||||
weight: weights[i],
|
Weight: weights[i],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
intersect := Intersect(aggregate, setParams...)
|
intersect := sorted_set.Intersect(aggregate, setParams...)
|
||||||
|
|
||||||
if server.KeyExists(ctx, destination) && intersect.Cardinality() > 0 {
|
if server.KeyExists(ctx, destination) && intersect.Cardinality() > 0 {
|
||||||
if _, err = server.KeyLock(ctx, destination); err != nil {
|
if _, err = server.KeyLock(ctx, destination); err != nil {
|
||||||
@@ -696,7 +701,7 @@ func handleZMPOP(ctx context.Context, cmd []string, server utils.EchoVault, conn
|
|||||||
if _, err = server.KeyLock(ctx, keys[i]); err != nil {
|
if _, err = server.KeyLock(ctx, keys[i]); err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
v, ok := server.GetValue(ctx, keys[i]).(*SortedSet)
|
v, ok := server.GetValue(ctx, keys[i]).(*sorted_set.SortedSet)
|
||||||
if !ok || v.Cardinality() == 0 {
|
if !ok || v.Cardinality() == 0 {
|
||||||
server.KeyUnlock(ctx, keys[i])
|
server.KeyUnlock(ctx, keys[i])
|
||||||
continue
|
continue
|
||||||
@@ -711,7 +716,7 @@ func handleZMPOP(ctx context.Context, cmd []string, server utils.EchoVault, conn
|
|||||||
res := fmt.Sprintf("*%d", popped.Cardinality())
|
res := fmt.Sprintf("*%d", popped.Cardinality())
|
||||||
|
|
||||||
for _, m := range popped.GetAll() {
|
for _, m := range popped.GetAll() {
|
||||||
res += fmt.Sprintf("\r\n*2\r\n$%d\r\n%s\r\n+%s", len(m.value), m.value, strconv.FormatFloat(float64(m.score), 'f', -1, 64))
|
res += fmt.Sprintf("\r\n*2\r\n$%d\r\n%s\r\n+%s", len(m.Value), m.Value, strconv.FormatFloat(float64(m.Score), 'f', -1, 64))
|
||||||
}
|
}
|
||||||
|
|
||||||
res += "\r\n"
|
res += "\r\n"
|
||||||
@@ -754,7 +759,7 @@ func handleZPOP(ctx context.Context, cmd []string, server utils.EchoVault, conn
|
|||||||
}
|
}
|
||||||
defer server.KeyUnlock(ctx, key)
|
defer server.KeyUnlock(ctx, key)
|
||||||
|
|
||||||
set, ok := server.GetValue(ctx, key).(*SortedSet)
|
set, ok := server.GetValue(ctx, key).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at key %s is not a sorted set", key)
|
return nil, fmt.Errorf("value at key %s is not a sorted set", key)
|
||||||
}
|
}
|
||||||
@@ -766,7 +771,7 @@ func handleZPOP(ctx context.Context, cmd []string, server utils.EchoVault, conn
|
|||||||
|
|
||||||
res := fmt.Sprintf("*%d", popped.Cardinality())
|
res := fmt.Sprintf("*%d", popped.Cardinality())
|
||||||
for _, m := range popped.GetAll() {
|
for _, m := range popped.GetAll() {
|
||||||
res += fmt.Sprintf("\r\n*2\r\n$%d\r\n%s\r\n+%s", len(m.value), m.value, strconv.FormatFloat(float64(m.score), 'f', -1, 64))
|
res += fmt.Sprintf("\r\n*2\r\n$%d\r\n%s\r\n+%s", len(m.Value), m.Value, strconv.FormatFloat(float64(m.Score), 'f', -1, 64))
|
||||||
}
|
}
|
||||||
|
|
||||||
res += "\r\n"
|
res += "\r\n"
|
||||||
@@ -791,7 +796,7 @@ func handleZMSCORE(ctx context.Context, cmd []string, server utils.EchoVault, co
|
|||||||
}
|
}
|
||||||
defer server.KeyRUnlock(ctx, key)
|
defer server.KeyRUnlock(ctx, key)
|
||||||
|
|
||||||
set, ok := server.GetValue(ctx, key).(*SortedSet)
|
set, ok := server.GetValue(ctx, key).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
||||||
}
|
}
|
||||||
@@ -800,14 +805,14 @@ func handleZMSCORE(ctx context.Context, cmd []string, server utils.EchoVault, co
|
|||||||
|
|
||||||
res := fmt.Sprintf("*%d", len(members))
|
res := fmt.Sprintf("*%d", len(members))
|
||||||
|
|
||||||
var member MemberObject
|
var member sorted_set.MemberObject
|
||||||
|
|
||||||
for i := 0; i < len(members); i++ {
|
for i := 0; i < len(members); i++ {
|
||||||
member = set.Get(Value(members[i]))
|
member = set.Get(sorted_set.Value(members[i]))
|
||||||
if !member.exists {
|
if !member.Exists {
|
||||||
res = fmt.Sprintf("%s\r\n$-1", res)
|
res = fmt.Sprintf("%s\r\n$-1", res)
|
||||||
} else {
|
} else {
|
||||||
res = fmt.Sprintf("%s\r\n+%s", res, strconv.FormatFloat(float64(member.score), 'f', -1, 64))
|
res = fmt.Sprintf("%s\r\n+%s", res, strconv.FormatFloat(float64(member.Score), 'f', -1, 64))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -850,7 +855,7 @@ func handleZRANDMEMBER(ctx context.Context, cmd []string, server utils.EchoVault
|
|||||||
}
|
}
|
||||||
defer server.KeyRUnlock(ctx, key)
|
defer server.KeyRUnlock(ctx, key)
|
||||||
|
|
||||||
set, ok := server.GetValue(ctx, key).(*SortedSet)
|
set, ok := server.GetValue(ctx, key).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
||||||
}
|
}
|
||||||
@@ -860,9 +865,9 @@ func handleZRANDMEMBER(ctx context.Context, cmd []string, server utils.EchoVault
|
|||||||
res := fmt.Sprintf("*%d", len(members))
|
res := fmt.Sprintf("*%d", len(members))
|
||||||
for _, m := range members {
|
for _, m := range members {
|
||||||
if withscores {
|
if withscores {
|
||||||
res += fmt.Sprintf("\r\n*2\r\n$%d\r\n%s\r\n+%s", len(m.value), m.value, strconv.FormatFloat(float64(m.score), 'f', -1, 64))
|
res += fmt.Sprintf("\r\n*2\r\n$%d\r\n%s\r\n+%s", len(m.Value), m.Value, strconv.FormatFloat(float64(m.Score), 'f', -1, 64))
|
||||||
} else {
|
} else {
|
||||||
res += fmt.Sprintf("\r\n*1\r\n$%d\r\n%s", len(m.value), m.value)
|
res += fmt.Sprintf("\r\n*1\r\n$%d\r\n%s", len(m.Value), m.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -871,7 +876,7 @@ func handleZRANDMEMBER(ctx context.Context, cmd []string, server utils.EchoVault
|
|||||||
return []byte(res), nil
|
return []byte(res), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleZRANK(ctx context.Context, cmd []string, server utils.EchoVault, conn *net.Conn) ([]byte, error) {
|
func handleZRANK(ctx context.Context, cmd []string, server utils.EchoVault, _ *net.Conn) ([]byte, error) {
|
||||||
keys, err := zrankKeyFunc(cmd)
|
keys, err := zrankKeyFunc(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -894,23 +899,23 @@ func handleZRANK(ctx context.Context, cmd []string, server utils.EchoVault, conn
|
|||||||
}
|
}
|
||||||
defer server.KeyRUnlock(ctx, key)
|
defer server.KeyRUnlock(ctx, key)
|
||||||
|
|
||||||
set, ok := server.GetValue(ctx, key).(*SortedSet)
|
set, ok := server.GetValue(ctx, key).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
members := set.GetAll()
|
members := set.GetAll()
|
||||||
slices.SortFunc(members, func(a, b MemberParam) int {
|
slices.SortFunc(members, func(a, b sorted_set.MemberParam) int {
|
||||||
if strings.EqualFold(cmd[0], "zrevrank") {
|
if strings.EqualFold(cmd[0], "zrevrank") {
|
||||||
return cmp.Compare(b.score, a.score)
|
return cmp.Compare(b.Score, a.Score)
|
||||||
}
|
}
|
||||||
return cmp.Compare(a.score, b.score)
|
return cmp.Compare(a.Score, b.Score)
|
||||||
})
|
})
|
||||||
|
|
||||||
for i := 0; i < len(members); i++ {
|
for i := 0; i < len(members); i++ {
|
||||||
if members[i].value == Value(member) {
|
if members[i].Value == sorted_set.Value(member) {
|
||||||
if withscores {
|
if withscores {
|
||||||
score := strconv.FormatFloat(float64(members[i].score), 'f', -1, 64)
|
score := strconv.FormatFloat(float64(members[i].Score), 'f', -1, 64)
|
||||||
return []byte(fmt.Sprintf("*2\r\n:%d\r\n$%d\r\n%s\r\n", i, len(score), score)), nil
|
return []byte(fmt.Sprintf("*2\r\n:%d\r\n$%d\r\n%s\r\n", i, len(score), score)), nil
|
||||||
} else {
|
} else {
|
||||||
return []byte(fmt.Sprintf("*1\r\n:%d\r\n", i)), nil
|
return []byte(fmt.Sprintf("*1\r\n:%d\r\n", i)), nil
|
||||||
@@ -938,14 +943,14 @@ func handleZREM(ctx context.Context, cmd []string, server utils.EchoVault, conn
|
|||||||
}
|
}
|
||||||
defer server.KeyUnlock(ctx, key)
|
defer server.KeyUnlock(ctx, key)
|
||||||
|
|
||||||
set, ok := server.GetValue(ctx, key).(*SortedSet)
|
set, ok := server.GetValue(ctx, key).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
deletedCount := 0
|
deletedCount := 0
|
||||||
for _, m := range cmd[2:] {
|
for _, m := range cmd[2:] {
|
||||||
if set.Remove(Value(m)) {
|
if set.Remove(sorted_set.Value(m)) {
|
||||||
deletedCount += 1
|
deletedCount += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -968,16 +973,16 @@ func handleZSCORE(ctx context.Context, cmd []string, server utils.EchoVault, con
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer server.KeyRUnlock(ctx, key)
|
defer server.KeyRUnlock(ctx, key)
|
||||||
set, ok := server.GetValue(ctx, key).(*SortedSet)
|
set, ok := server.GetValue(ctx, key).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
||||||
}
|
}
|
||||||
member := set.Get(Value(cmd[2]))
|
member := set.Get(sorted_set.Value(cmd[2]))
|
||||||
if !member.exists {
|
if !member.Exists {
|
||||||
return []byte("$-1\r\n"), nil
|
return []byte("$-1\r\n"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
score := strconv.FormatFloat(float64(member.score), 'f', -1, 64)
|
score := strconv.FormatFloat(float64(member.Score), 'f', -1, 64)
|
||||||
|
|
||||||
return []byte(fmt.Sprintf("$%d\r\n%s\r\n", len(score), score)), nil
|
return []byte(fmt.Sprintf("$%d\r\n%s\r\n", len(score), score)), nil
|
||||||
}
|
}
|
||||||
@@ -1011,14 +1016,14 @@ func handleZREMRANGEBYSCORE(ctx context.Context, cmd []string, server utils.Echo
|
|||||||
}
|
}
|
||||||
defer server.KeyUnlock(ctx, key)
|
defer server.KeyUnlock(ctx, key)
|
||||||
|
|
||||||
set, ok := server.GetValue(ctx, key).(*SortedSet)
|
set, ok := server.GetValue(ctx, key).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, m := range set.GetAll() {
|
for _, m := range set.GetAll() {
|
||||||
if m.score >= Score(minimum) && m.score <= Score(maximum) {
|
if m.Score >= sorted_set.Score(minimum) && m.Score <= sorted_set.Score(maximum) {
|
||||||
set.Remove(m.value)
|
set.Remove(m.Value)
|
||||||
deletedCount += 1
|
deletedCount += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1053,7 +1058,7 @@ func handleZREMRANGEBYRANK(ctx context.Context, cmd []string, server utils.EchoV
|
|||||||
}
|
}
|
||||||
defer server.KeyUnlock(ctx, key)
|
defer server.KeyUnlock(ctx, key)
|
||||||
|
|
||||||
set, ok := server.GetValue(ctx, key).(*SortedSet)
|
set, ok := server.GetValue(ctx, key).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
||||||
}
|
}
|
||||||
@@ -1070,20 +1075,20 @@ func handleZREMRANGEBYRANK(ctx context.Context, cmd []string, server utils.EchoV
|
|||||||
}
|
}
|
||||||
|
|
||||||
members := set.GetAll()
|
members := set.GetAll()
|
||||||
slices.SortFunc(members, func(a, b MemberParam) int {
|
slices.SortFunc(members, func(a, b sorted_set.MemberParam) int {
|
||||||
return cmp.Compare(a.score, b.score)
|
return cmp.Compare(a.Score, b.Score)
|
||||||
})
|
})
|
||||||
|
|
||||||
deletedCount := 0
|
deletedCount := 0
|
||||||
|
|
||||||
if start < stop {
|
if start < stop {
|
||||||
for i := start; i <= stop; i++ {
|
for i := start; i <= stop; i++ {
|
||||||
set.Remove(members[i].value)
|
set.Remove(members[i].Value)
|
||||||
deletedCount += 1
|
deletedCount += 1
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for i := stop; i <= start; i++ {
|
for i := stop; i <= start; i++ {
|
||||||
set.Remove(members[i].value)
|
set.Remove(members[i].Value)
|
||||||
deletedCount += 1
|
deletedCount += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1110,7 +1115,7 @@ func handleZREMRANGEBYLEX(ctx context.Context, cmd []string, server utils.EchoVa
|
|||||||
}
|
}
|
||||||
defer server.KeyUnlock(ctx, key)
|
defer server.KeyUnlock(ctx, key)
|
||||||
|
|
||||||
set, ok := server.GetValue(ctx, key).(*SortedSet)
|
set, ok := server.GetValue(ctx, key).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
||||||
}
|
}
|
||||||
@@ -1119,7 +1124,7 @@ func handleZREMRANGEBYLEX(ctx context.Context, cmd []string, server utils.EchoVa
|
|||||||
|
|
||||||
// Check if all the members have the same score. If not, return 0
|
// Check if all the members have the same score. If not, return 0
|
||||||
for i := 0; i < len(members)-1; i++ {
|
for i := 0; i < len(members)-1; i++ {
|
||||||
if members[i].score != members[i+1].score {
|
if members[i].Score != members[i+1].Score {
|
||||||
return []byte(":0\r\n"), nil
|
return []byte(":0\r\n"), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1128,9 +1133,9 @@ func handleZREMRANGEBYLEX(ctx context.Context, cmd []string, server utils.EchoVa
|
|||||||
|
|
||||||
// All the members have the same score
|
// All the members have the same score
|
||||||
for _, m := range members {
|
for _, m := range members {
|
||||||
if slices.Contains([]int{1, 0}, compareLex(string(m.value), minimum)) &&
|
if slices.Contains([]int{1, 0}, internal.CompareLex(string(m.Value), minimum)) &&
|
||||||
slices.Contains([]int{-1, 0}, compareLex(string(m.value), maximum)) {
|
slices.Contains([]int{-1, 0}, internal.CompareLex(string(m.Value), maximum)) {
|
||||||
set.Remove(m.value)
|
set.Remove(m.Value)
|
||||||
deletedCount += 1
|
deletedCount += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1208,7 +1213,7 @@ func handleZRANGE(ctx context.Context, cmd []string, server utils.EchoVault, con
|
|||||||
}
|
}
|
||||||
defer server.KeyRUnlock(ctx, key)
|
defer server.KeyRUnlock(ctx, key)
|
||||||
|
|
||||||
set, ok := server.GetValue(ctx, key).(*SortedSet)
|
set, ok := server.GetValue(ctx, key).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
return nil, fmt.Errorf("value at %s is not a sorted set", key)
|
||||||
}
|
}
|
||||||
@@ -1222,43 +1227,43 @@ func handleZRANGE(ctx context.Context, cmd []string, server utils.EchoVault, con
|
|||||||
|
|
||||||
members := set.GetAll()
|
members := set.GetAll()
|
||||||
if strings.EqualFold(policy, "byscore") {
|
if strings.EqualFold(policy, "byscore") {
|
||||||
slices.SortFunc(members, func(a, b MemberParam) int {
|
slices.SortFunc(members, func(a, b sorted_set.MemberParam) int {
|
||||||
// Do a score sort
|
// Do a score sort
|
||||||
if reverse {
|
if reverse {
|
||||||
return cmp.Compare(b.score, a.score)
|
return cmp.Compare(b.Score, a.Score)
|
||||||
}
|
}
|
||||||
return cmp.Compare(a.score, b.score)
|
return cmp.Compare(a.Score, b.Score)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if strings.EqualFold(policy, "bylex") {
|
if strings.EqualFold(policy, "bylex") {
|
||||||
// If policy is BYLEX, all the elements must have the same score
|
// If policy is BYLEX, all the elements must have the same score
|
||||||
for i := 0; i < len(members)-1; i++ {
|
for i := 0; i < len(members)-1; i++ {
|
||||||
if members[i].score != members[i+1].score {
|
if members[i].Score != members[i+1].Score {
|
||||||
return []byte("*0\r\n"), nil
|
return []byte("*0\r\n"), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slices.SortFunc(members, func(a, b MemberParam) int {
|
slices.SortFunc(members, func(a, b sorted_set.MemberParam) int {
|
||||||
if reverse {
|
if reverse {
|
||||||
return compareLex(string(b.value), string(a.value))
|
return internal.CompareLex(string(b.Value), string(a.Value))
|
||||||
}
|
}
|
||||||
return compareLex(string(a.value), string(b.value))
|
return internal.CompareLex(string(a.Value), string(b.Value))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var resultMembers []MemberParam
|
var resultMembers []sorted_set.MemberParam
|
||||||
|
|
||||||
for i := offset; i <= count; i++ {
|
for i := offset; i <= count; i++ {
|
||||||
if i >= len(members) {
|
if i >= len(members) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if strings.EqualFold(policy, "byscore") {
|
if strings.EqualFold(policy, "byscore") {
|
||||||
if members[i].score >= Score(scoreStart) && members[i].score <= Score(scoreStop) {
|
if members[i].Score >= sorted_set.Score(scoreStart) && members[i].Score <= sorted_set.Score(scoreStop) {
|
||||||
resultMembers = append(resultMembers, members[i])
|
resultMembers = append(resultMembers, members[i])
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if slices.Contains([]int{1, 0}, compareLex(string(members[i].value), lexStart)) &&
|
if slices.Contains([]int{1, 0}, internal.CompareLex(string(members[i].Value), lexStart)) &&
|
||||||
slices.Contains([]int{-1, 0}, compareLex(string(members[i].value), lexStop)) {
|
slices.Contains([]int{-1, 0}, internal.CompareLex(string(members[i].Value), lexStop)) {
|
||||||
resultMembers = append(resultMembers, members[i])
|
resultMembers = append(resultMembers, members[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1267,9 +1272,9 @@ func handleZRANGE(ctx context.Context, cmd []string, server utils.EchoVault, con
|
|||||||
|
|
||||||
for _, m := range resultMembers {
|
for _, m := range resultMembers {
|
||||||
if withscores {
|
if withscores {
|
||||||
res += fmt.Sprintf("\r\n*2\r\n$%d\r\n%s\r\n+%s", len(m.value), m.value, strconv.FormatFloat(float64(m.score), 'f', -1, 64))
|
res += fmt.Sprintf("\r\n*2\r\n$%d\r\n%s\r\n+%s", len(m.Value), m.Value, strconv.FormatFloat(float64(m.Score), 'f', -1, 64))
|
||||||
} else {
|
} else {
|
||||||
res += fmt.Sprintf("\r\n*1\r\n$%d\r\n%s", len(m.value), m.value)
|
res += fmt.Sprintf("\r\n*1\r\n$%d\r\n%s", len(m.Value), m.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1345,7 +1350,7 @@ func handleZRANGESTORE(ctx context.Context, cmd []string, server utils.EchoVault
|
|||||||
}
|
}
|
||||||
defer server.KeyRUnlock(ctx, source)
|
defer server.KeyRUnlock(ctx, source)
|
||||||
|
|
||||||
set, ok := server.GetValue(ctx, source).(*SortedSet)
|
set, ok := server.GetValue(ctx, source).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", source)
|
return nil, fmt.Errorf("value at %s is not a sorted set", source)
|
||||||
}
|
}
|
||||||
@@ -1359,48 +1364,48 @@ func handleZRANGESTORE(ctx context.Context, cmd []string, server utils.EchoVault
|
|||||||
|
|
||||||
members := set.GetAll()
|
members := set.GetAll()
|
||||||
if strings.EqualFold(policy, "byscore") {
|
if strings.EqualFold(policy, "byscore") {
|
||||||
slices.SortFunc(members, func(a, b MemberParam) int {
|
slices.SortFunc(members, func(a, b sorted_set.MemberParam) int {
|
||||||
// Do a score sort
|
// Do a score sort
|
||||||
if reverse {
|
if reverse {
|
||||||
return cmp.Compare(b.score, a.score)
|
return cmp.Compare(b.Score, a.Score)
|
||||||
}
|
}
|
||||||
return cmp.Compare(a.score, b.score)
|
return cmp.Compare(a.Score, b.Score)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if strings.EqualFold(policy, "bylex") {
|
if strings.EqualFold(policy, "bylex") {
|
||||||
// If policy is BYLEX, all the elements must have the same score
|
// If policy is BYLEX, all the elements must have the same score
|
||||||
for i := 0; i < len(members)-1; i++ {
|
for i := 0; i < len(members)-1; i++ {
|
||||||
if members[i].score != members[i+1].score {
|
if members[i].Score != members[i+1].Score {
|
||||||
return []byte(":0\r\n"), nil
|
return []byte(":0\r\n"), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slices.SortFunc(members, func(a, b MemberParam) int {
|
slices.SortFunc(members, func(a, b sorted_set.MemberParam) int {
|
||||||
if reverse {
|
if reverse {
|
||||||
return compareLex(string(b.value), string(a.value))
|
return internal.CompareLex(string(b.Value), string(a.Value))
|
||||||
}
|
}
|
||||||
return compareLex(string(a.value), string(b.value))
|
return internal.CompareLex(string(a.Value), string(b.Value))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var resultMembers []MemberParam
|
var resultMembers []sorted_set.MemberParam
|
||||||
|
|
||||||
for i := offset; i <= count; i++ {
|
for i := offset; i <= count; i++ {
|
||||||
if i >= len(members) {
|
if i >= len(members) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if strings.EqualFold(policy, "byscore") {
|
if strings.EqualFold(policy, "byscore") {
|
||||||
if members[i].score >= Score(scoreStart) && members[i].score <= Score(scoreStop) {
|
if members[i].Score >= sorted_set.Score(scoreStart) && members[i].Score <= sorted_set.Score(scoreStop) {
|
||||||
resultMembers = append(resultMembers, members[i])
|
resultMembers = append(resultMembers, members[i])
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if slices.Contains([]int{1, 0}, compareLex(string(members[i].value), lexStart)) &&
|
if slices.Contains([]int{1, 0}, internal.CompareLex(string(members[i].Value), lexStart)) &&
|
||||||
slices.Contains([]int{-1, 0}, compareLex(string(members[i].value), lexStop)) {
|
slices.Contains([]int{-1, 0}, internal.CompareLex(string(members[i].Value), lexStop)) {
|
||||||
resultMembers = append(resultMembers, members[i])
|
resultMembers = append(resultMembers, members[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newSortedSet := NewSortedSet(resultMembers)
|
newSortedSet := sorted_set.NewSortedSet(resultMembers)
|
||||||
|
|
||||||
if server.KeyExists(ctx, destination) {
|
if server.KeyExists(ctx, destination) {
|
||||||
if _, err = server.KeyLock(ctx, destination); err != nil {
|
if _, err = server.KeyLock(ctx, destination); err != nil {
|
||||||
@@ -1439,7 +1444,7 @@ func handleZUNION(ctx context.Context, cmd []string, server utils.EchoVault, con
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var setParams []SortedSetParam
|
var setParams []sorted_set.SortedSetParam
|
||||||
|
|
||||||
for i := 0; i < len(keys); i++ {
|
for i := 0; i < len(keys); i++ {
|
||||||
if server.KeyExists(ctx, keys[i]) {
|
if server.KeyExists(ctx, keys[i]) {
|
||||||
@@ -1447,25 +1452,25 @@ func handleZUNION(ctx context.Context, cmd []string, server utils.EchoVault, con
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
locks[keys[i]] = true
|
locks[keys[i]] = true
|
||||||
set, ok := server.GetValue(ctx, keys[i]).(*SortedSet)
|
set, ok := server.GetValue(ctx, keys[i]).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", keys[i])
|
return nil, fmt.Errorf("value at %s is not a sorted set", keys[i])
|
||||||
}
|
}
|
||||||
setParams = append(setParams, SortedSetParam{
|
setParams = append(setParams, sorted_set.SortedSetParam{
|
||||||
set: set,
|
Set: set,
|
||||||
weight: weights[i],
|
Weight: weights[i],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
union := Union(aggregate, setParams...)
|
union := sorted_set.Union(aggregate, setParams...)
|
||||||
|
|
||||||
res := fmt.Sprintf("*%d", union.Cardinality())
|
res := fmt.Sprintf("*%d", union.Cardinality())
|
||||||
for _, m := range union.GetAll() {
|
for _, m := range union.GetAll() {
|
||||||
if withscores {
|
if withscores {
|
||||||
res += fmt.Sprintf("\r\n*2\r\n$%d\r\n%s\r\n+%s", len(m.value), m.value, strconv.FormatFloat(float64(m.score), 'f', -1, 64))
|
res += fmt.Sprintf("\r\n*2\r\n$%d\r\n%s\r\n+%s", len(m.Value), m.Value, strconv.FormatFloat(float64(m.Score), 'f', -1, 64))
|
||||||
} else {
|
} else {
|
||||||
res += fmt.Sprintf("\r\n*1\r\n$%d\r\n%s", len(m.value), m.value)
|
res += fmt.Sprintf("\r\n*1\r\n$%d\r\n%s", len(m.Value), m.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1501,7 +1506,7 @@ func handleZUNIONSTORE(ctx context.Context, cmd []string, server utils.EchoVault
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var setParams []SortedSetParam
|
var setParams []sorted_set.SortedSetParam
|
||||||
|
|
||||||
for i := 0; i < len(keys); i++ {
|
for i := 0; i < len(keys); i++ {
|
||||||
if server.KeyExists(ctx, keys[i]) {
|
if server.KeyExists(ctx, keys[i]) {
|
||||||
@@ -1509,18 +1514,18 @@ func handleZUNIONSTORE(ctx context.Context, cmd []string, server utils.EchoVault
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
locks[keys[i]] = true
|
locks[keys[i]] = true
|
||||||
set, ok := server.GetValue(ctx, keys[i]).(*SortedSet)
|
set, ok := server.GetValue(ctx, keys[i]).(*sorted_set.SortedSet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("value at %s is not a sorted set", keys[i])
|
return nil, fmt.Errorf("value at %s is not a sorted set", keys[i])
|
||||||
}
|
}
|
||||||
setParams = append(setParams, SortedSetParam{
|
setParams = append(setParams, sorted_set.SortedSetParam{
|
||||||
set: set,
|
Set: set,
|
||||||
weight: weights[i],
|
Weight: weights[i],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
union := Union(aggregate, setParams...)
|
union := sorted_set.Union(aggregate, setParams...)
|
||||||
|
|
||||||
if server.KeyExists(ctx, destination) {
|
if server.KeyExists(ctx, destination) {
|
||||||
if _, err = server.KeyLock(ctx, destination); err != nil {
|
if _, err = server.KeyLock(ctx, destination); err != nil {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,6 @@
|
|||||||
package sorted_set
|
package sorted_set
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmp"
|
|
||||||
"errors"
|
"errors"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -91,109 +90,3 @@ func extractKeysWeightsAggregateWithScores(cmd []string) ([]string, []int, strin
|
|||||||
|
|
||||||
return keys, weights, aggregate, withscores, nil
|
return keys, weights, aggregate, withscores, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateUpdatePolicy(updatePolicy interface{}) (string, error) {
|
|
||||||
if updatePolicy == nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
err := errors.New("update policy must be a string of value NX or XX")
|
|
||||||
policy, ok := updatePolicy.(string)
|
|
||||||
if !ok {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if !slices.Contains([]string{"nx", "xx"}, strings.ToLower(policy)) {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return policy, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateComparison(comparison interface{}) (string, error) {
|
|
||||||
if comparison == nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
err := errors.New("comparison condition must be a string of value LT or GT")
|
|
||||||
comp, ok := comparison.(string)
|
|
||||||
if !ok {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if !slices.Contains([]string{"lt", "gt"}, strings.ToLower(comp)) {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return comp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateChanged(changed interface{}) (string, error) {
|
|
||||||
if changed == nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
err := errors.New("changed condition should be a string of value CH")
|
|
||||||
ch, ok := changed.(string)
|
|
||||||
if !ok {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if !strings.EqualFold(ch, "ch") {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return ch, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateIncr(incr interface{}) (string, error) {
|
|
||||||
if incr == nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
err := errors.New("incr condition should be a string of value INCR")
|
|
||||||
i, ok := incr.(string)
|
|
||||||
if !ok {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if !strings.EqualFold(i, "incr") {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return i, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func compareScores(old Score, new Score, comp string) Score {
|
|
||||||
switch strings.ToLower(comp) {
|
|
||||||
default:
|
|
||||||
return new
|
|
||||||
case "lt":
|
|
||||||
if new < old {
|
|
||||||
return new
|
|
||||||
}
|
|
||||||
return old
|
|
||||||
case "gt":
|
|
||||||
if new > old {
|
|
||||||
return new
|
|
||||||
}
|
|
||||||
return old
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// compareLex returns -1 when s2 is lexicographically greater than s1,
|
|
||||||
// 0 if they're equal and 1 if s2 is lexicographically less than s1.
|
|
||||||
func compareLex(s1 string, s2 string) int {
|
|
||||||
if s1 == s2 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if strings.Contains(s1, s2) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
if strings.Contains(s2, s1) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
limit := len(s1)
|
|
||||||
if len(s2) < limit {
|
|
||||||
limit = len(s2)
|
|
||||||
}
|
|
||||||
|
|
||||||
var c int
|
|
||||||
for i := 0; i < limit; i++ {
|
|
||||||
c = cmp.Compare(s1[i], s2[i])
|
|
||||||
if c != 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user