mirror of
https://github.com/HDT3213/godis.git
synced 2025-10-05 16:57:06 +08:00
add zlexcount, zremrangebylex, zrankbylex, zrevrankbylex to zset
This commit is contained in:
@@ -1,14 +1,15 @@
|
|||||||
package aof
|
package aof
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/hdt3213/godis/datastruct/dict"
|
"github.com/hdt3213/godis/datastruct/dict"
|
||||||
List "github.com/hdt3213/godis/datastruct/list"
|
List "github.com/hdt3213/godis/datastruct/list"
|
||||||
"github.com/hdt3213/godis/datastruct/set"
|
"github.com/hdt3213/godis/datastruct/set"
|
||||||
SortedSet "github.com/hdt3213/godis/datastruct/sortedset"
|
SortedSet "github.com/hdt3213/godis/datastruct/sortedset"
|
||||||
"github.com/hdt3213/godis/interface/database"
|
"github.com/hdt3213/godis/interface/database"
|
||||||
"github.com/hdt3213/godis/redis/protocol"
|
"github.com/hdt3213/godis/redis/protocol"
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// EntityToCmd serialize data entity to redis command
|
// EntityToCmd serialize data entity to redis command
|
||||||
@@ -95,7 +96,7 @@ func zSetToCmd(key string, zset *SortedSet.SortedSet) *protocol.MultiBulkReply {
|
|||||||
args[0] = zAddCmd
|
args[0] = zAddCmd
|
||||||
args[1] = []byte(key)
|
args[1] = []byte(key)
|
||||||
i := 0
|
i := 0
|
||||||
zset.ForEach(int64(0), int64(zset.Len()), true, func(element *SortedSet.Element) bool {
|
zset.ForEachByRank(int64(0), int64(zset.Len()), true, func(element *SortedSet.Element) bool {
|
||||||
value := strconv.FormatFloat(element.Score, 'f', -1, 64)
|
value := strconv.FormatFloat(element.Score, 'f', -1, 64)
|
||||||
args[2+i*2] = []byte(value)
|
args[2+i*2] = []byte(value)
|
||||||
args[3+i*2] = []byte(element.Member)
|
args[3+i*2] = []byte(element.Member)
|
||||||
|
@@ -168,7 +168,7 @@ func (persister *Persister) generateRDB(ctx *RewriteCtx) error {
|
|||||||
err = encoder.WriteHashMapObject(key, hash, opts...)
|
err = encoder.WriteHashMapObject(key, hash, opts...)
|
||||||
case *SortedSet.SortedSet:
|
case *SortedSet.SortedSet:
|
||||||
var entries []*model.ZSetEntry
|
var entries []*model.ZSetEntry
|
||||||
obj.ForEach(int64(0), obj.Len(), true, func(element *SortedSet.Element) bool {
|
obj.ForEachByRank(int64(0), obj.Len(), true, func(element *SortedSet.Element) bool {
|
||||||
entries = append(entries, &model.ZSetEntry{
|
entries = append(entries, &model.ZSetEntry{
|
||||||
Member: element.Member,
|
Member: element.Member,
|
||||||
Score: element.Score,
|
Score: element.Score,
|
||||||
|
@@ -97,6 +97,10 @@
|
|||||||
- zrem
|
- zrem
|
||||||
- zremrangebyscore
|
- zremrangebyscore
|
||||||
- zremrangebyrank
|
- zremrangebyrank
|
||||||
|
- zlexcount
|
||||||
|
- zrangebylex
|
||||||
|
- zremrangebylex
|
||||||
|
- zrevrangebylex
|
||||||
- Pub / Sub
|
- Pub / Sub
|
||||||
- publish
|
- publish
|
||||||
- subscribe
|
- subscribe
|
||||||
|
@@ -2,13 +2,14 @@ package database
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/hdt3213/godis/datastruct/sortedset"
|
"github.com/hdt3213/godis/datastruct/sortedset"
|
||||||
"github.com/hdt3213/godis/interface/redis"
|
"github.com/hdt3213/godis/interface/redis"
|
||||||
"github.com/hdt3213/godis/lib/geohash"
|
"github.com/hdt3213/godis/lib/geohash"
|
||||||
"github.com/hdt3213/godis/lib/utils"
|
"github.com/hdt3213/godis/lib/utils"
|
||||||
"github.com/hdt3213/godis/redis/protocol"
|
"github.com/hdt3213/godis/redis/protocol"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// execGeoAdd add a location into SortedSet
|
// execGeoAdd add a location into SortedSet
|
||||||
@@ -253,7 +254,7 @@ func geoRadius0(sortedSet *sortedset.SortedSet, lat float64, lng float64, radius
|
|||||||
for _, area := range areas {
|
for _, area := range areas {
|
||||||
lower := &sortedset.ScoreBorder{Value: float64(area[0])}
|
lower := &sortedset.ScoreBorder{Value: float64(area[0])}
|
||||||
upper := &sortedset.ScoreBorder{Value: float64(area[1])}
|
upper := &sortedset.ScoreBorder{Value: float64(area[1])}
|
||||||
elements := sortedSet.RangeByScore(lower, upper, 0, -1, true)
|
elements := sortedSet.Range(lower, upper, 0, -1, true)
|
||||||
for _, elem := range elements {
|
for _, elem := range elements {
|
||||||
members = append(members, []byte(elem.Member))
|
members = append(members, []byte(elem.Member))
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,15 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
SortedSet "github.com/hdt3213/godis/datastruct/sortedset"
|
SortedSet "github.com/hdt3213/godis/datastruct/sortedset"
|
||||||
"github.com/hdt3213/godis/interface/database"
|
"github.com/hdt3213/godis/interface/database"
|
||||||
"github.com/hdt3213/godis/interface/redis"
|
"github.com/hdt3213/godis/interface/redis"
|
||||||
"github.com/hdt3213/godis/lib/utils"
|
"github.com/hdt3213/godis/lib/utils"
|
||||||
"github.com/hdt3213/godis/redis/protocol"
|
"github.com/hdt3213/godis/redis/protocol"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (db *DB) getAsSortedSet(key string) (*SortedSet.SortedSet, protocol.ErrorReply) {
|
func (db *DB) getAsSortedSet(key string) (*SortedSet.SortedSet, protocol.ErrorReply) {
|
||||||
@@ -253,7 +255,7 @@ func range0(db *DB, key string, start int64, stop int64, withScores bool, desc b
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assert: start in [0, size - 1], stop in [start, size]
|
// assert: start in [0, size - 1], stop in [start, size]
|
||||||
slice := sortedSet.Range(start, stop, desc)
|
slice := sortedSet.RangeByRank(start, stop, desc)
|
||||||
if withScores {
|
if withScores {
|
||||||
result := make([][]byte, len(slice)*2)
|
result := make([][]byte, len(slice)*2)
|
||||||
i := 0
|
i := 0
|
||||||
@@ -298,13 +300,13 @@ func execZCount(db *DB, args [][]byte) redis.Reply {
|
|||||||
return protocol.MakeIntReply(0)
|
return protocol.MakeIntReply(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return protocol.MakeIntReply(sortedSet.Count(min, max))
|
return protocol.MakeIntReply(sortedSet.RangeCount(min, max))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* param limit: limit < 0 means no limit
|
* param limit: limit < 0 means no limit
|
||||||
*/
|
*/
|
||||||
func rangeByScore0(db *DB, key string, min *SortedSet.ScoreBorder, max *SortedSet.ScoreBorder, offset int64, limit int64, withScores bool, desc bool) redis.Reply {
|
func rangeByScore0(db *DB, key string, min SortedSet.Border, max SortedSet.Border, offset int64, limit int64, withScores bool, desc bool) redis.Reply {
|
||||||
// get data
|
// get data
|
||||||
sortedSet, errReply := db.getAsSortedSet(key)
|
sortedSet, errReply := db.getAsSortedSet(key)
|
||||||
if errReply != nil {
|
if errReply != nil {
|
||||||
@@ -314,7 +316,7 @@ func rangeByScore0(db *DB, key string, min *SortedSet.ScoreBorder, max *SortedSe
|
|||||||
return &protocol.EmptyMultiBulkReply{}
|
return &protocol.EmptyMultiBulkReply{}
|
||||||
}
|
}
|
||||||
|
|
||||||
slice := sortedSet.RangeByScore(min, max, offset, limit, desc)
|
slice := sortedSet.Range(min, max, offset, limit, desc)
|
||||||
if withScores {
|
if withScores {
|
||||||
result := make([][]byte, len(slice)*2)
|
result := make([][]byte, len(slice)*2)
|
||||||
i := 0
|
i := 0
|
||||||
@@ -456,7 +458,7 @@ func execZRemRangeByScore(db *DB, args [][]byte) redis.Reply {
|
|||||||
return &protocol.EmptyMultiBulkReply{}
|
return &protocol.EmptyMultiBulkReply{}
|
||||||
}
|
}
|
||||||
|
|
||||||
removed := sortedSet.RemoveByScore(min, max)
|
removed := sortedSet.RemoveRange(min, max)
|
||||||
if removed > 0 {
|
if removed > 0 {
|
||||||
db.addAof(utils.ToCmdLine3("zremrangebyscore", args...))
|
db.addAof(utils.ToCmdLine3("zremrangebyscore", args...))
|
||||||
}
|
}
|
||||||
@@ -621,6 +623,179 @@ func undoZIncr(db *DB, args [][]byte) []CmdLine {
|
|||||||
return rollbackZSetFields(db, key, field)
|
return rollbackZSetFields(db, key, field)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func execZLexCount(db *DB, args [][]byte) redis.Reply {
|
||||||
|
key := string(args[0])
|
||||||
|
sortedSet, errReply := db.getAsSortedSet(key)
|
||||||
|
if errReply != nil {
|
||||||
|
return errReply
|
||||||
|
}
|
||||||
|
if sortedSet == nil {
|
||||||
|
return protocol.MakeIntReply(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
minEle, maxEle := string(args[1]), string(args[2])
|
||||||
|
min, err := SortedSet.ParseLexBorder(minEle)
|
||||||
|
if err != nil {
|
||||||
|
return protocol.MakeErrReply(err.Error())
|
||||||
|
}
|
||||||
|
max, err := SortedSet.ParseLexBorder(maxEle)
|
||||||
|
if err != nil {
|
||||||
|
return protocol.MakeErrReply(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
count := sortedSet.RangeCount(min, max)
|
||||||
|
|
||||||
|
return protocol.MakeIntReply(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func execZRangeByLex(db *DB, args [][]byte) redis.Reply {
|
||||||
|
n := len(args)
|
||||||
|
if n > 3 && strings.ToLower(string(args[3])) != "limit" {
|
||||||
|
return protocol.MakeErrReply("ERR syntax error")
|
||||||
|
}
|
||||||
|
if n != 3 && n != 6 {
|
||||||
|
return protocol.MakeErrReply("ERR wrong number of arguments for 'zrangebylex' command")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := string(args[0])
|
||||||
|
sortedSet, errReply := db.getAsSortedSet(key)
|
||||||
|
if errReply != nil {
|
||||||
|
return errReply
|
||||||
|
}
|
||||||
|
if sortedSet == nil {
|
||||||
|
return protocol.MakeIntReply(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
minEle, maxEle := string(args[1]), string(args[2])
|
||||||
|
min, err := SortedSet.ParseLexBorder(minEle)
|
||||||
|
if err != nil {
|
||||||
|
return protocol.MakeErrReply(err.Error())
|
||||||
|
}
|
||||||
|
max, err := SortedSet.ParseLexBorder(maxEle)
|
||||||
|
if err != nil {
|
||||||
|
return protocol.MakeErrReply(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := int64(0)
|
||||||
|
limitCnt := int64(math.MaxInt64)
|
||||||
|
if n > 3 {
|
||||||
|
var err error
|
||||||
|
offset, err = strconv.ParseInt(string(args[4]), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return protocol.MakeErrReply("ERR value is not an integer or out of range")
|
||||||
|
}
|
||||||
|
if offset < 0 {
|
||||||
|
return protocol.MakeEmptyMultiBulkReply()
|
||||||
|
}
|
||||||
|
count, err := strconv.ParseInt(string(args[5]), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return protocol.MakeErrReply("ERR value is not an integer or out of range")
|
||||||
|
}
|
||||||
|
if count >= 0 {
|
||||||
|
limitCnt = count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elements := sortedSet.Range(min, max, offset, limitCnt, false)
|
||||||
|
result := make([][]byte, 0, len(elements))
|
||||||
|
for _, ele := range elements {
|
||||||
|
result = append(result, []byte(ele.Member))
|
||||||
|
}
|
||||||
|
if len(result) == 0 {
|
||||||
|
return protocol.MakeEmptyMultiBulkReply()
|
||||||
|
}
|
||||||
|
return protocol.MakeMultiBulkReply(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func execZRemRangeByLex(db *DB, args [][]byte) redis.Reply {
|
||||||
|
n := len(args)
|
||||||
|
if n != 3 {
|
||||||
|
return protocol.MakeErrReply("ERR wrong number of arguments for 'zremrangebylex' command")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := string(args[0])
|
||||||
|
sortedSet, errReply := db.getAsSortedSet(key)
|
||||||
|
if errReply != nil {
|
||||||
|
return errReply
|
||||||
|
}
|
||||||
|
if sortedSet == nil {
|
||||||
|
return protocol.MakeIntReply(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
minEle, maxEle := string(args[1]), string(args[2])
|
||||||
|
min, err := SortedSet.ParseLexBorder(minEle)
|
||||||
|
if err != nil {
|
||||||
|
return protocol.MakeErrReply(err.Error())
|
||||||
|
}
|
||||||
|
max, err := SortedSet.ParseLexBorder(maxEle)
|
||||||
|
if err != nil {
|
||||||
|
return protocol.MakeErrReply(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
count := sortedSet.RemoveRange(min, max)
|
||||||
|
|
||||||
|
return protocol.MakeIntReply(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func execZRevRangeByLex(db *DB, args [][]byte) redis.Reply {
|
||||||
|
n := len(args)
|
||||||
|
if n > 3 && strings.ToLower(string(args[3])) != "limit" {
|
||||||
|
return protocol.MakeErrReply("ERR syntax error")
|
||||||
|
}
|
||||||
|
if n != 3 && n != 6 {
|
||||||
|
return protocol.MakeErrReply("ERR wrong number of arguments for 'zrangebylex' command")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := string(args[0])
|
||||||
|
sortedSet, errReply := db.getAsSortedSet(key)
|
||||||
|
if errReply != nil {
|
||||||
|
return errReply
|
||||||
|
}
|
||||||
|
if sortedSet == nil {
|
||||||
|
return protocol.MakeIntReply(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
minEle, maxEle := string(args[2]), string(args[1])
|
||||||
|
min, err := SortedSet.ParseLexBorder(minEle)
|
||||||
|
if err != nil {
|
||||||
|
return protocol.MakeErrReply(err.Error())
|
||||||
|
}
|
||||||
|
max, err := SortedSet.ParseLexBorder(maxEle)
|
||||||
|
if err != nil {
|
||||||
|
return protocol.MakeErrReply(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := int64(0)
|
||||||
|
limitCnt := int64(math.MaxInt64)
|
||||||
|
if n > 3 {
|
||||||
|
var err error
|
||||||
|
offset, err = strconv.ParseInt(string(args[4]), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return protocol.MakeErrReply("ERR value is not an integer or out of range")
|
||||||
|
}
|
||||||
|
if offset < 0 {
|
||||||
|
return protocol.MakeEmptyMultiBulkReply()
|
||||||
|
}
|
||||||
|
count, err := strconv.ParseInt(string(args[5]), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return protocol.MakeErrReply("ERR value is not an integer or out of range")
|
||||||
|
}
|
||||||
|
if count >= 0 {
|
||||||
|
limitCnt = count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elements := sortedSet.Range(min, max, offset, limitCnt, true)
|
||||||
|
result := make([][]byte, 0, len(elements))
|
||||||
|
for _, ele := range elements {
|
||||||
|
result = append(result, []byte(ele.Member))
|
||||||
|
}
|
||||||
|
if len(result) == 0 {
|
||||||
|
return protocol.MakeEmptyMultiBulkReply()
|
||||||
|
}
|
||||||
|
return protocol.MakeMultiBulkReply(result)
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registerCommand("ZAdd", execZAdd, writeFirstKey, undoZAdd, -4, flagWrite).
|
registerCommand("ZAdd", execZAdd, writeFirstKey, undoZAdd, -4, flagWrite).
|
||||||
attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM, redisFlagFast}, 1, 1, 1)
|
attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM, redisFlagFast}, 1, 1, 1)
|
||||||
@@ -652,4 +827,12 @@ func init() {
|
|||||||
attachCommandExtra([]string{redisFlagWrite}, 1, 1, 1)
|
attachCommandExtra([]string{redisFlagWrite}, 1, 1, 1)
|
||||||
registerCommand("ZRemRangeByRank", execZRemRangeByRank, writeFirstKey, rollbackFirstKey, 4, flagWrite).
|
registerCommand("ZRemRangeByRank", execZRemRangeByRank, writeFirstKey, rollbackFirstKey, 4, flagWrite).
|
||||||
attachCommandExtra([]string{redisFlagWrite}, 1, 1, 1)
|
attachCommandExtra([]string{redisFlagWrite}, 1, 1, 1)
|
||||||
|
registerCommand("ZLexCount", execZLexCount, readFirstKey, nil, 4, flagReadOnly).
|
||||||
|
attachCommandExtra([]string{redisFlagReadonly}, 1, 1, 1)
|
||||||
|
registerCommand("ZRangeByLex", execZRangeByLex, readFirstKey, nil, -4, flagReadOnly).
|
||||||
|
attachCommandExtra([]string{redisFlagReadonly}, 1, 1, 1)
|
||||||
|
registerCommand("ZRemRangeByLex", execZRemRangeByLex, writeFirstKey, rollbackFirstKey, 4, flagWrite).
|
||||||
|
attachCommandExtra([]string{redisFlagWrite}, 1, 1, 1)
|
||||||
|
registerCommand("ZRevRangeByLex", execZRevRangeByLex, readFirstKey, nil, -4, flagReadOnly).
|
||||||
|
attachCommandExtra([]string{redisFlagReadonly}, 1, 1, 1)
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/hdt3213/godis/lib/utils"
|
|
||||||
"github.com/hdt3213/godis/redis/protocol/asserts"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hdt3213/godis/lib/utils"
|
||||||
|
"github.com/hdt3213/godis/redis/protocol/asserts"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestZAdd(t *testing.T) {
|
func TestZAdd(t *testing.T) {
|
||||||
@@ -321,3 +322,444 @@ func TestZPopMin(t *testing.T) {
|
|||||||
result = testDB.Exec(nil, utils.ToCmdLine("ZPopMin", key+"2", "2"))
|
result = testDB.Exec(nil, utils.ToCmdLine("ZPopMin", key+"2", "2"))
|
||||||
asserts.AssertErrReply(t, result, "WRONGTYPE Operation against a key holding the wrong kind of value")
|
asserts.AssertErrReply(t, result, "WRONGTYPE Operation against a key holding the wrong kind of value")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestZLexCount(t *testing.T) {
|
||||||
|
testDB.Flush()
|
||||||
|
key := utils.RandString(10)
|
||||||
|
// a b c d e
|
||||||
|
result := testDB.Exec(nil, utils.ToCmdLine("ZAdd", key, "0", "e", "0", "d", "0", "c", "0", "b", "0", "a"))
|
||||||
|
asserts.AssertNotError(t, result)
|
||||||
|
|
||||||
|
// case1
|
||||||
|
result1 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "(-", "(+"))
|
||||||
|
asserts.AssertIntReply(t, result1, 0)
|
||||||
|
|
||||||
|
// case2
|
||||||
|
result2 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "(-", "(g"))
|
||||||
|
asserts.AssertIntReply(t, result2, 5)
|
||||||
|
|
||||||
|
// case3
|
||||||
|
result3 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "(-", "(c"))
|
||||||
|
asserts.AssertIntReply(t, result3, 2)
|
||||||
|
|
||||||
|
// case4
|
||||||
|
result4 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "(-", "[c"))
|
||||||
|
asserts.AssertIntReply(t, result4, 3)
|
||||||
|
|
||||||
|
// case5
|
||||||
|
result5 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "(a", "(+"))
|
||||||
|
asserts.AssertIntReply(t, result5, 0)
|
||||||
|
|
||||||
|
// case6
|
||||||
|
result6 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "[-", "[+"))
|
||||||
|
asserts.AssertIntReply(t, result6, 0)
|
||||||
|
|
||||||
|
// case
|
||||||
|
result7 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "[-", "(g"))
|
||||||
|
asserts.AssertIntReply(t, result7, 5)
|
||||||
|
|
||||||
|
// case8
|
||||||
|
result8 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "[-", "(c"))
|
||||||
|
asserts.AssertIntReply(t, result8, 2)
|
||||||
|
|
||||||
|
// case9
|
||||||
|
result9 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "[-", "[c"))
|
||||||
|
asserts.AssertIntReply(t, result9, 3)
|
||||||
|
|
||||||
|
// case10
|
||||||
|
result10 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "(a", "[+"))
|
||||||
|
asserts.AssertIntReply(t, result10, 0)
|
||||||
|
|
||||||
|
// case11
|
||||||
|
result11 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "-", "+"))
|
||||||
|
asserts.AssertIntReply(t, result11, 5)
|
||||||
|
|
||||||
|
// case12
|
||||||
|
result12 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "-", "(c"))
|
||||||
|
asserts.AssertIntReply(t, result12, 2)
|
||||||
|
|
||||||
|
// case13
|
||||||
|
result13 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "-", "[c"))
|
||||||
|
asserts.AssertIntReply(t, result13, 3)
|
||||||
|
|
||||||
|
// case14
|
||||||
|
result14 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "(aa", "(c"))
|
||||||
|
asserts.AssertIntReply(t, result14, 1)
|
||||||
|
|
||||||
|
// case15
|
||||||
|
result15 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "(aa", "[c"))
|
||||||
|
asserts.AssertIntReply(t, result15, 2)
|
||||||
|
|
||||||
|
// case16
|
||||||
|
result16 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "[aa", "(c"))
|
||||||
|
asserts.AssertIntReply(t, result16, 1)
|
||||||
|
|
||||||
|
// case17
|
||||||
|
result17 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "[aa", "[c"))
|
||||||
|
asserts.AssertIntReply(t, result17, 2)
|
||||||
|
|
||||||
|
// case18
|
||||||
|
result18 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "(a", "(ee"))
|
||||||
|
asserts.AssertIntReply(t, result18, 4)
|
||||||
|
|
||||||
|
// case19
|
||||||
|
result19 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "(a", "[ee"))
|
||||||
|
asserts.AssertIntReply(t, result19, 4)
|
||||||
|
|
||||||
|
// case20
|
||||||
|
result20 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "[a", "(ee"))
|
||||||
|
asserts.AssertIntReply(t, result20, 5)
|
||||||
|
|
||||||
|
// case21
|
||||||
|
result21 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "[a", "[ee"))
|
||||||
|
asserts.AssertIntReply(t, result21, 5)
|
||||||
|
|
||||||
|
// case22
|
||||||
|
result22 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "(aa", "(ee"))
|
||||||
|
asserts.AssertIntReply(t, result22, 4)
|
||||||
|
|
||||||
|
// case23
|
||||||
|
result23 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "(aa", "[ee"))
|
||||||
|
asserts.AssertIntReply(t, result23, 4)
|
||||||
|
|
||||||
|
// case24
|
||||||
|
result24 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "[aa", "(ee"))
|
||||||
|
asserts.AssertIntReply(t, result24, 4)
|
||||||
|
|
||||||
|
// case25
|
||||||
|
result25 := testDB.Exec(nil, utils.ToCmdLine("ZLexCount", key, "[aa", "[ee"))
|
||||||
|
asserts.AssertIntReply(t, result25, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZRangeByLex(t *testing.T) {
|
||||||
|
testDB.Flush()
|
||||||
|
key := utils.RandString(10)
|
||||||
|
// a b c d e
|
||||||
|
result := testDB.Exec(nil, utils.ToCmdLine("ZAdd", key, "0", "e", "0", "d", "0", "c", "0", "b", "0", "a"))
|
||||||
|
asserts.AssertNotError(t, result)
|
||||||
|
|
||||||
|
// case1
|
||||||
|
result1 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "-", "+"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result1, []string{"a", "b", "c", "d", "e"})
|
||||||
|
|
||||||
|
// case2
|
||||||
|
result2 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "-", "(z"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result2, []string{"a", "b", "c", "d", "e"})
|
||||||
|
|
||||||
|
// case3
|
||||||
|
result3 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "(-", "[z"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result3, []string{"a", "b", "c", "d", "e"})
|
||||||
|
|
||||||
|
// case4
|
||||||
|
result4 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "[a", "[e"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result4, []string{"a", "b", "c", "d", "e"})
|
||||||
|
|
||||||
|
// case5
|
||||||
|
result5 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "(a", "[e"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result5, []string{"b", "c", "d", "e"})
|
||||||
|
|
||||||
|
// case6
|
||||||
|
result6 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "[a", "(e"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result6, []string{"a", "b", "c", "d"})
|
||||||
|
|
||||||
|
// case7
|
||||||
|
result7 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "(a", "(e"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result7, []string{"b", "c", "d"})
|
||||||
|
|
||||||
|
// case8
|
||||||
|
result8 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "(aa", "(ee"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result8, []string{"b", "c", "d", "e"})
|
||||||
|
|
||||||
|
// case9
|
||||||
|
result9 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "(aa", "[ee"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result9, []string{"b", "c", "d", "e"})
|
||||||
|
|
||||||
|
// case10
|
||||||
|
result10 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "[aa", "(ee"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result10, []string{"b", "c", "d", "e"})
|
||||||
|
|
||||||
|
// case11
|
||||||
|
result11 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "[aa", "[ee"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result11, []string{"b", "c", "d", "e"})
|
||||||
|
|
||||||
|
// case12
|
||||||
|
result12 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "(aa", "(e"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result12, []string{"b", "c", "d"})
|
||||||
|
|
||||||
|
// case13
|
||||||
|
result13 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "(aa", "[e"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result13, []string{"b", "c", "d", "e"})
|
||||||
|
|
||||||
|
// case14
|
||||||
|
result14 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "[aa", "(e"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result14, []string{"b", "c", "d"})
|
||||||
|
|
||||||
|
// case15
|
||||||
|
result15 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "[aa", "[e"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result15, []string{"b", "c", "d", "e"})
|
||||||
|
|
||||||
|
// case16
|
||||||
|
result16 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "(a", "(ee"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result16, []string{"b", "c", "d", "e"})
|
||||||
|
|
||||||
|
// case17
|
||||||
|
result17 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "(a", "[ee"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result17, []string{"b", "c", "d", "e"})
|
||||||
|
|
||||||
|
// case18
|
||||||
|
result18 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "[a", "(ee"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result18, []string{"a", "b", "c", "d", "e"})
|
||||||
|
|
||||||
|
// case19
|
||||||
|
result19 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "[a", "[ee"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result19, []string{"a", "b", "c", "d", "e"})
|
||||||
|
|
||||||
|
// case20
|
||||||
|
result20 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "(-", "(+"))
|
||||||
|
asserts.AssertMultiBulkReplySize(t, result20, 0)
|
||||||
|
|
||||||
|
// case21
|
||||||
|
result21 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "(a", "(+"))
|
||||||
|
asserts.AssertMultiBulkReplySize(t, result21, 0)
|
||||||
|
|
||||||
|
// case22
|
||||||
|
result22 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "[-", "[+"))
|
||||||
|
asserts.AssertMultiBulkReplySize(t, result22, 0)
|
||||||
|
|
||||||
|
// case23
|
||||||
|
result23 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "(z", "(g"))
|
||||||
|
asserts.AssertMultiBulkReplySize(t, result23, 0)
|
||||||
|
|
||||||
|
// case24
|
||||||
|
result24 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "[-", "(g"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result24, []string{"a", "b", "c", "d", "e"})
|
||||||
|
|
||||||
|
// case25
|
||||||
|
result25 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "[-", "(c"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result25, []string{"a", "b"})
|
||||||
|
|
||||||
|
// case26
|
||||||
|
result26 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "[-", "[c"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result26, []string{"a", "b", "c"})
|
||||||
|
|
||||||
|
// case27
|
||||||
|
result27 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "(a", "(e", "limit", "0", "-1"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result27, []string{"b", "c", "d"})
|
||||||
|
|
||||||
|
// case28
|
||||||
|
result28 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "(a", "(e", "limit", "0", "1"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result28, []string{"b"})
|
||||||
|
|
||||||
|
// case28
|
||||||
|
result29 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "(a", "(e", "limit", "-1", "1"))
|
||||||
|
asserts.AssertMultiBulkReplySize(t, result29, 0)
|
||||||
|
|
||||||
|
// case30
|
||||||
|
result30 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "[a", "[e", "limit", "2", "100"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result30, []string{"c", "d", "e"})
|
||||||
|
|
||||||
|
// case30
|
||||||
|
result31 := testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "-", "+", "limit", "2", "2"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result31, []string{"c", "d"})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZRemRangeByLex(t *testing.T) {
|
||||||
|
testDB.Flush()
|
||||||
|
key := utils.RandString(10)
|
||||||
|
// a b c d e
|
||||||
|
asserts.AssertNotError(t, testDB.Exec(nil, utils.ToCmdLine("ZAdd", key, "0", "e", "0", "d", "0", "c", "0", "b", "0", "a")))
|
||||||
|
|
||||||
|
// case1
|
||||||
|
asserts.AssertIntReply(t,
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZRemRangeByLex", key, "-", "+")),
|
||||||
|
5)
|
||||||
|
|
||||||
|
asserts.AssertMultiBulkReplySize(t,
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "-", "+")),
|
||||||
|
0)
|
||||||
|
|
||||||
|
// case2
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZAdd", key, "0", "e", "0", "d", "0", "c", "0", "b", "0", "a"))
|
||||||
|
|
||||||
|
asserts.AssertIntReply(t,
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZRemRangeByLex", key, "-", "[c")),
|
||||||
|
3)
|
||||||
|
|
||||||
|
asserts.AssertMultiBulkReply(t,
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "-", "+")),
|
||||||
|
[]string{"d", "e"})
|
||||||
|
|
||||||
|
// case3
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZAdd", key, "0", "a", "0", "b", "0", "c"))
|
||||||
|
|
||||||
|
asserts.AssertMultiBulkReply(t,
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "-", "+")),
|
||||||
|
[]string{"a", "b", "c", "d", "e"})
|
||||||
|
|
||||||
|
asserts.AssertIntReply(t,
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZRemRangeByLex", key, "(c", "+")),
|
||||||
|
2)
|
||||||
|
|
||||||
|
asserts.AssertMultiBulkReply(t,
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "-", "+")),
|
||||||
|
[]string{"a", "b", "c"})
|
||||||
|
|
||||||
|
// case4
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZAdd", key, "0", "d", "0", "e"))
|
||||||
|
|
||||||
|
asserts.AssertMultiBulkReply(t,
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "-", "+")),
|
||||||
|
[]string{"a", "b", "c", "d", "e"})
|
||||||
|
|
||||||
|
asserts.AssertIntReply(t,
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZRemRangeByLex", key, "(a", "(d")),
|
||||||
|
2)
|
||||||
|
|
||||||
|
asserts.AssertMultiBulkReply(t,
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "-", "+")),
|
||||||
|
[]string{"a", "d", "e"})
|
||||||
|
|
||||||
|
// case5
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZAdd", key, "0", "b", "0", "c"))
|
||||||
|
|
||||||
|
asserts.AssertMultiBulkReply(t,
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "-", "+")),
|
||||||
|
[]string{"a", "b", "c", "d", "e"})
|
||||||
|
|
||||||
|
asserts.AssertIntReply(t,
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZRemRangeByLex", key, "[a", "[d")),
|
||||||
|
4)
|
||||||
|
|
||||||
|
asserts.AssertMultiBulkReply(t,
|
||||||
|
testDB.Exec(nil, utils.ToCmdLine("ZRangeByLex", key, "-", "+")),
|
||||||
|
[]string{"e"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZRevRangeByLex(t *testing.T) {
|
||||||
|
testDB.Flush()
|
||||||
|
key := utils.RandString(10)
|
||||||
|
// a b c d e
|
||||||
|
result := testDB.Exec(nil, utils.ToCmdLine("ZAdd", key, "0", "e", "0", "d", "0", "c", "0", "b", "0", "a"))
|
||||||
|
asserts.AssertNotError(t, result)
|
||||||
|
|
||||||
|
// case1
|
||||||
|
result1 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "+", "-"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result1, []string{"e", "d", "c", "b", "a"})
|
||||||
|
|
||||||
|
// case2
|
||||||
|
result2 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "(z", "-"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result2, []string{"e", "d", "c", "b", "a"})
|
||||||
|
|
||||||
|
// case3
|
||||||
|
result3 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "[z", "-"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result3, []string{"e", "d", "c", "b", "a"})
|
||||||
|
|
||||||
|
// case4
|
||||||
|
result4 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "[e", "[a"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result4, []string{"e", "d", "c", "b", "a"})
|
||||||
|
|
||||||
|
// case5
|
||||||
|
result5 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "[e", "(a"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result5, []string{"e", "d", "c", "b"})
|
||||||
|
|
||||||
|
// case6
|
||||||
|
result6 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "(e", "[a"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result6, []string{"d", "c", "b", "a"})
|
||||||
|
|
||||||
|
// case7
|
||||||
|
result7 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "(e", "(a"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result7, []string{"d", "c", "b"})
|
||||||
|
|
||||||
|
// case8
|
||||||
|
result8 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "(ee", "(aa"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result8, []string{"e", "d", "c", "b"})
|
||||||
|
|
||||||
|
// case9
|
||||||
|
result9 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "[ee", "(aa"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result9, []string{"e", "d", "c", "b"})
|
||||||
|
|
||||||
|
// case10
|
||||||
|
result10 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "(ee", "[aa"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result10, []string{"e", "d", "c", "b"})
|
||||||
|
|
||||||
|
// case11
|
||||||
|
result11 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "[ee", "[aa"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result11, []string{"e", "d", "c", "b"})
|
||||||
|
|
||||||
|
// case12
|
||||||
|
result12 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "(e", "(aa"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result12, []string{"d", "c", "b"})
|
||||||
|
|
||||||
|
// case13
|
||||||
|
result13 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "[e", "(aa"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result13, []string{"e", "d", "c", "b"})
|
||||||
|
|
||||||
|
// case14
|
||||||
|
result14 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "(e", "[aa"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result14, []string{"d", "c", "b"})
|
||||||
|
|
||||||
|
// case15
|
||||||
|
result15 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "[e", "[aa"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result15, []string{"e", "d", "c", "b"})
|
||||||
|
|
||||||
|
// case16
|
||||||
|
result16 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "(ee", "(a"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result16, []string{"e", "d", "c", "b"})
|
||||||
|
|
||||||
|
// case17
|
||||||
|
result17 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "[ee", "(a"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result17, []string{"e", "d", "c", "b"})
|
||||||
|
|
||||||
|
// case18
|
||||||
|
result18 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "(ee", "[a"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result18, []string{"e", "d", "c", "b", "a"})
|
||||||
|
|
||||||
|
// case19
|
||||||
|
result19 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "[ee", "[a"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result19, []string{"e", "d", "c", "b", "a"})
|
||||||
|
|
||||||
|
// case20
|
||||||
|
result20 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "(+", "(-"))
|
||||||
|
asserts.AssertMultiBulkReplySize(t, result20, 0)
|
||||||
|
|
||||||
|
// case21
|
||||||
|
result21 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "(+", "(a"))
|
||||||
|
asserts.AssertMultiBulkReplySize(t, result21, 0)
|
||||||
|
|
||||||
|
// case22
|
||||||
|
result22 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "[+", "[-"))
|
||||||
|
asserts.AssertMultiBulkReplySize(t, result22, 0)
|
||||||
|
|
||||||
|
// case23
|
||||||
|
result23 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "(g", "[-"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result23, []string{"e", "d", "c", "b", "a"})
|
||||||
|
|
||||||
|
// case24
|
||||||
|
result24 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "(c", "[-"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result24, []string{"b", "a"})
|
||||||
|
|
||||||
|
// case25
|
||||||
|
result25 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "[c", "[-"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result25, []string{"c", "b", "a"})
|
||||||
|
|
||||||
|
// case26
|
||||||
|
result26 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "(e", "(a", "limit", "0", "-1"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result26, []string{"d", "c", "b"})
|
||||||
|
|
||||||
|
// case27
|
||||||
|
result27 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "(e", "(a", "limit", "0", "1"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result27, []string{"d"})
|
||||||
|
|
||||||
|
// case28
|
||||||
|
result28 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "(e", "(a", "limit", "-1", "1"))
|
||||||
|
asserts.AssertMultiBulkReplySize(t, result28, 0)
|
||||||
|
|
||||||
|
// case29
|
||||||
|
result29 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "[e", "[a", "limit", "2", "100"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result29, []string{"c", "b", "a"})
|
||||||
|
|
||||||
|
// case30
|
||||||
|
result30 := testDB.Exec(nil, utils.ToCmdLine("ZRevRangeByLex", key, "+", "-", "limit", "2", "2"))
|
||||||
|
asserts.AssertMultiBulkReply(t, result30, []string{"c", "b"})
|
||||||
|
}
|
||||||
|
@@ -14,10 +14,20 @@ import (
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const (
|
const (
|
||||||
negativeInf int8 = -1
|
scoreNegativeInf int8 = -1
|
||||||
positiveInf int8 = 1
|
scorePositiveInf int8 = 1
|
||||||
|
lexNegativeInf int8 = '-'
|
||||||
|
lexPositiveInf int8 = '+'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Border interface {
|
||||||
|
greater(element *Element) bool
|
||||||
|
less(element *Element) bool
|
||||||
|
getValue() interface{}
|
||||||
|
getExclude() bool
|
||||||
|
isIntersected(max Border) bool
|
||||||
|
}
|
||||||
|
|
||||||
// ScoreBorder represents range of a float value, including: <, <=, >, >=, +inf, -inf
|
// ScoreBorder represents range of a float value, including: <, <=, >, >=, +inf, -inf
|
||||||
type ScoreBorder struct {
|
type ScoreBorder struct {
|
||||||
Inf int8
|
Inf int8
|
||||||
@@ -27,10 +37,11 @@ type ScoreBorder struct {
|
|||||||
|
|
||||||
// if max.greater(score) then the score is within the upper border
|
// if max.greater(score) then the score is within the upper border
|
||||||
// do not use min.greater()
|
// do not use min.greater()
|
||||||
func (border *ScoreBorder) greater(value float64) bool {
|
func (border *ScoreBorder) greater(element *Element) bool {
|
||||||
if border.Inf == negativeInf {
|
value := element.Score
|
||||||
|
if border.Inf == scoreNegativeInf {
|
||||||
return false
|
return false
|
||||||
} else if border.Inf == positiveInf {
|
} else if border.Inf == scorePositiveInf {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if border.Exclude {
|
if border.Exclude {
|
||||||
@@ -39,10 +50,11 @@ func (border *ScoreBorder) greater(value float64) bool {
|
|||||||
return border.Value >= value
|
return border.Value >= value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (border *ScoreBorder) less(value float64) bool {
|
func (border *ScoreBorder) less(element *Element) bool {
|
||||||
if border.Inf == negativeInf {
|
value := element.Score
|
||||||
|
if border.Inf == scoreNegativeInf {
|
||||||
return true
|
return true
|
||||||
} else if border.Inf == positiveInf {
|
} else if border.Inf == scorePositiveInf {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if border.Exclude {
|
if border.Exclude {
|
||||||
@@ -51,21 +63,29 @@ func (border *ScoreBorder) less(value float64) bool {
|
|||||||
return border.Value <= value
|
return border.Value <= value
|
||||||
}
|
}
|
||||||
|
|
||||||
var positiveInfBorder = &ScoreBorder{
|
func (border *ScoreBorder) getValue() interface{} {
|
||||||
Inf: positiveInf,
|
return border.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
var negativeInfBorder = &ScoreBorder{
|
func (border *ScoreBorder) getExclude() bool {
|
||||||
Inf: negativeInf,
|
return border.Exclude
|
||||||
|
}
|
||||||
|
|
||||||
|
var scorePositiveInfBorder = &ScoreBorder{
|
||||||
|
Inf: scorePositiveInf,
|
||||||
|
}
|
||||||
|
|
||||||
|
var scoreNegativeInfBorder = &ScoreBorder{
|
||||||
|
Inf: scoreNegativeInf,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseScoreBorder creates ScoreBorder from redis arguments
|
// ParseScoreBorder creates ScoreBorder from redis arguments
|
||||||
func ParseScoreBorder(s string) (*ScoreBorder, error) {
|
func ParseScoreBorder(s string) (Border, error) {
|
||||||
if s == "inf" || s == "+inf" {
|
if s == "inf" || s == "+inf" {
|
||||||
return positiveInfBorder, nil
|
return scorePositiveInfBorder, nil
|
||||||
}
|
}
|
||||||
if s == "-inf" {
|
if s == "-inf" {
|
||||||
return negativeInfBorder, nil
|
return scoreNegativeInfBorder, nil
|
||||||
}
|
}
|
||||||
if s[0] == '(' {
|
if s[0] == '(' {
|
||||||
value, err := strconv.ParseFloat(s[1:], 64)
|
value, err := strconv.ParseFloat(s[1:], 64)
|
||||||
@@ -88,3 +108,93 @@ func ParseScoreBorder(s string) (*ScoreBorder, error) {
|
|||||||
Exclude: false,
|
Exclude: false,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (border *ScoreBorder) isIntersected(max Border) bool {
|
||||||
|
minValue := border.Value
|
||||||
|
maxValue := max.(*ScoreBorder).Value
|
||||||
|
return minValue > maxValue || (minValue == maxValue && (border.getExclude() || max.getExclude()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LexBorder represents range of a string value, including: <, <=, >, >=, +, -
|
||||||
|
type LexBorder struct {
|
||||||
|
Inf int8
|
||||||
|
Value string
|
||||||
|
Exclude bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// if max.greater(lex) then the lex is within the upper border
|
||||||
|
// do not use min.greater()
|
||||||
|
func (border *LexBorder) greater(element *Element) bool {
|
||||||
|
value := element.Member
|
||||||
|
if border.Inf == lexNegativeInf {
|
||||||
|
return false
|
||||||
|
} else if border.Inf == lexPositiveInf {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if border.Exclude {
|
||||||
|
return border.Value > value
|
||||||
|
}
|
||||||
|
return border.Value >= value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (border *LexBorder) less(element *Element) bool {
|
||||||
|
value := element.Member
|
||||||
|
if border.Inf == lexNegativeInf {
|
||||||
|
return true
|
||||||
|
} else if border.Inf == lexPositiveInf {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if border.Exclude {
|
||||||
|
return border.Value < value
|
||||||
|
}
|
||||||
|
return border.Value <= value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (border *LexBorder) getValue() interface{} {
|
||||||
|
return border.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (border *LexBorder) getExclude() bool {
|
||||||
|
return border.Exclude
|
||||||
|
}
|
||||||
|
|
||||||
|
var lexPositiveInfBorder = &LexBorder{
|
||||||
|
Inf: lexPositiveInf,
|
||||||
|
}
|
||||||
|
|
||||||
|
var lexNegativeInfBorder = &LexBorder{
|
||||||
|
Inf: lexNegativeInf,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseLexBorder creates LexBorder from redis arguments
|
||||||
|
func ParseLexBorder(s string) (Border, error) {
|
||||||
|
if s == "+" {
|
||||||
|
return lexPositiveInfBorder, nil
|
||||||
|
}
|
||||||
|
if s == "-" {
|
||||||
|
return lexNegativeInfBorder, nil
|
||||||
|
}
|
||||||
|
if s[0] == '(' {
|
||||||
|
return &LexBorder{
|
||||||
|
Inf: 0,
|
||||||
|
Value: s[1:],
|
||||||
|
Exclude: true,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if s[0] == '[' {
|
||||||
|
return &LexBorder{
|
||||||
|
Inf: 0,
|
||||||
|
Value: s[1:],
|
||||||
|
Exclude: false,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("ERR min or max not valid string range item")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (border *LexBorder) isIntersected(max Border) bool {
|
||||||
|
minValue := border.Value
|
||||||
|
maxValue := max.(*LexBorder).Value
|
||||||
|
return border.Inf == '+' || minValue > maxValue || (minValue == maxValue && (border.getExclude() || max.getExclude()))
|
||||||
|
}
|
||||||
|
@@ -221,25 +221,25 @@ func (skiplist *skiplist) getByRank(rank int64) *node {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (skiplist *skiplist) hasInRange(min *ScoreBorder, max *ScoreBorder) bool {
|
func (skiplist *skiplist) hasInRange(min Border, max Border) bool {
|
||||||
// min & max = empty
|
if min.isIntersected(max) { //是有交集的,则返回false
|
||||||
if min.Value > max.Value || (min.Value == max.Value && (min.Exclude || max.Exclude)) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// min > tail
|
// min > tail
|
||||||
n := skiplist.tail
|
n := skiplist.tail
|
||||||
if n == nil || !min.less(n.Score) {
|
if n == nil || !min.less(&n.Element) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// max < head
|
// max < head
|
||||||
n = skiplist.header.level[0].forward
|
n = skiplist.header.level[0].forward
|
||||||
if n == nil || !max.greater(n.Score) {
|
if n == nil || !max.greater(&n.Element) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (skiplist *skiplist) getFirstInScoreRange(min *ScoreBorder, max *ScoreBorder) *node {
|
func (skiplist *skiplist) getFirstInRange(min Border, max Border) *node {
|
||||||
if !skiplist.hasInRange(min, max) {
|
if !skiplist.hasInRange(min, max) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -247,30 +247,30 @@ func (skiplist *skiplist) getFirstInScoreRange(min *ScoreBorder, max *ScoreBorde
|
|||||||
// scan from top level
|
// scan from top level
|
||||||
for level := skiplist.level - 1; level >= 0; level-- {
|
for level := skiplist.level - 1; level >= 0; level-- {
|
||||||
// if forward is not in range than move forward
|
// if forward is not in range than move forward
|
||||||
for n.level[level].forward != nil && !min.less(n.level[level].forward.Score) {
|
for n.level[level].forward != nil && !min.less(&n.level[level].forward.Element) {
|
||||||
n = n.level[level].forward
|
n = n.level[level].forward
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* This is an inner range, so the next node cannot be NULL. */
|
/* This is an inner range, so the next node cannot be NULL. */
|
||||||
n = n.level[0].forward
|
n = n.level[0].forward
|
||||||
if !max.greater(n.Score) {
|
if !max.greater(&n.Element) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (skiplist *skiplist) getLastInScoreRange(min *ScoreBorder, max *ScoreBorder) *node {
|
func (skiplist *skiplist) getLastInRange(min Border, max Border) *node {
|
||||||
if !skiplist.hasInRange(min, max) {
|
if !skiplist.hasInRange(min, max) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
n := skiplist.header
|
n := skiplist.header
|
||||||
// scan from top level
|
// scan from top level
|
||||||
for level := skiplist.level - 1; level >= 0; level-- {
|
for level := skiplist.level - 1; level >= 0; level-- {
|
||||||
for n.level[level].forward != nil && max.greater(n.level[level].forward.Score) {
|
for n.level[level].forward != nil && max.greater(&n.level[level].forward.Element) {
|
||||||
n = n.level[level].forward
|
n = n.level[level].forward
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !min.less(n.Score) {
|
if !min.less(&n.Element) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
@@ -279,14 +279,14 @@ func (skiplist *skiplist) getLastInScoreRange(min *ScoreBorder, max *ScoreBorder
|
|||||||
/*
|
/*
|
||||||
* return removed elements
|
* return removed elements
|
||||||
*/
|
*/
|
||||||
func (skiplist *skiplist) RemoveRangeByScore(min *ScoreBorder, max *ScoreBorder, limit int) (removed []*Element) {
|
func (skiplist *skiplist) RemoveRange(min Border, max Border, limit int) (removed []*Element) {
|
||||||
update := make([]*node, maxLevel)
|
update := make([]*node, maxLevel)
|
||||||
removed = make([]*Element, 0)
|
removed = make([]*Element, 0)
|
||||||
// find backward nodes (of target range) or last node of each level
|
// find backward nodes (of target range) or last node of each level
|
||||||
node := skiplist.header
|
node := skiplist.header
|
||||||
for i := skiplist.level - 1; i >= 0; i-- {
|
for i := skiplist.level - 1; i >= 0; i-- {
|
||||||
for node.level[i].forward != nil {
|
for node.level[i].forward != nil {
|
||||||
if min.less(node.level[i].forward.Score) { // already in range
|
if min.less(&node.level[i].forward.Element) { // already in range
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
node = node.level[i].forward
|
node = node.level[i].forward
|
||||||
@@ -299,7 +299,7 @@ func (skiplist *skiplist) RemoveRangeByScore(min *ScoreBorder, max *ScoreBorder,
|
|||||||
|
|
||||||
// remove nodes in range
|
// remove nodes in range
|
||||||
for node != nil {
|
for node != nil {
|
||||||
if !max.greater(node.Score) { // already out of range
|
if !max.greater(&node.Element) { // already out of range
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
next := node.level[0].forward
|
next := node.level[0].forward
|
||||||
|
@@ -18,7 +18,7 @@ func Make() *SortedSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add puts member into set, and returns whether has inserted new node
|
// Add puts member into set, and returns whether it has inserted new node
|
||||||
func (sortedSet *SortedSet) Add(member string, score float64) bool {
|
func (sortedSet *SortedSet) Add(member string, score float64) bool {
|
||||||
element, ok := sortedSet.dict[member]
|
element, ok := sortedSet.dict[member]
|
||||||
sortedSet.dict[member] = &Element{
|
sortedSet.dict[member] = &Element{
|
||||||
@@ -76,8 +76,8 @@ func (sortedSet *SortedSet) GetRank(member string, desc bool) (rank int64) {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForEach visits each member which rank within [start, stop), sort by ascending order, rank starts from 0
|
// ForEachByRank visits each member which rank within [start, stop), sort by ascending order, rank starts from 0
|
||||||
func (sortedSet *SortedSet) ForEach(start int64, stop int64, desc bool, consumer func(element *Element) bool) {
|
func (sortedSet *SortedSet) ForEachByRank(start int64, stop int64, desc bool, consumer func(element *Element) bool) {
|
||||||
size := int64(sortedSet.Len())
|
size := int64(sortedSet.Len())
|
||||||
if start < 0 || start >= size {
|
if start < 0 || start >= size {
|
||||||
panic("illegal start " + strconv.FormatInt(start, 10))
|
panic("illegal start " + strconv.FormatInt(start, 10))
|
||||||
@@ -113,12 +113,12 @@ func (sortedSet *SortedSet) ForEach(start int64, stop int64, desc bool, consumer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Range returns members which rank within [start, stop), sort by ascending order, rank starts from 0
|
// RangeByRank returns members which rank within [start, stop), sort by ascending order, rank starts from 0
|
||||||
func (sortedSet *SortedSet) Range(start int64, stop int64, desc bool) []*Element {
|
func (sortedSet *SortedSet) RangeByRank(start int64, stop int64, desc bool) []*Element {
|
||||||
sliceSize := int(stop - start)
|
sliceSize := int(stop - start)
|
||||||
slice := make([]*Element, sliceSize)
|
slice := make([]*Element, sliceSize)
|
||||||
i := 0
|
i := 0
|
||||||
sortedSet.ForEach(start, stop, desc, func(element *Element) bool {
|
sortedSet.ForEachByRank(start, stop, desc, func(element *Element) bool {
|
||||||
slice[i] = element
|
slice[i] = element
|
||||||
i++
|
i++
|
||||||
return true
|
return true
|
||||||
@@ -126,17 +126,17 @@ func (sortedSet *SortedSet) Range(start int64, stop int64, desc bool) []*Element
|
|||||||
return slice
|
return slice
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count returns the number of members which score within the given border
|
// RangeCount returns the number of members which score or member within the given border
|
||||||
func (sortedSet *SortedSet) Count(min *ScoreBorder, max *ScoreBorder) int64 {
|
func (sortedSet *SortedSet) RangeCount(min Border, max Border) int64 {
|
||||||
var i int64 = 0
|
var i int64 = 0
|
||||||
// ascending order
|
// ascending order
|
||||||
sortedSet.ForEach(0, sortedSet.Len(), false, func(element *Element) bool {
|
sortedSet.ForEachByRank(0, sortedSet.Len(), false, func(element *Element) bool {
|
||||||
gtMin := min.less(element.Score) // greater than min
|
gtMin := min.less(element) // greater than min
|
||||||
if !gtMin {
|
if !gtMin {
|
||||||
// has not into range, continue foreach
|
// has not into range, continue foreach
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
ltMax := max.greater(element.Score) // less than max
|
ltMax := max.greater(element) // less than max
|
||||||
if !ltMax {
|
if !ltMax {
|
||||||
// break through score border, break foreach
|
// break through score border, break foreach
|
||||||
return false
|
return false
|
||||||
@@ -148,14 +148,14 @@ func (sortedSet *SortedSet) Count(min *ScoreBorder, max *ScoreBorder) int64 {
|
|||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForEachByScore visits members which score within the given border
|
// ForEach visits members which score or member within the given border
|
||||||
func (sortedSet *SortedSet) ForEachByScore(min *ScoreBorder, max *ScoreBorder, offset int64, limit int64, desc bool, consumer func(element *Element) bool) {
|
func (sortedSet *SortedSet) ForEach(min Border, max Border, offset int64, limit int64, desc bool, consumer func(element *Element) bool) {
|
||||||
// find start node
|
// find start node
|
||||||
var node *node
|
var node *node
|
||||||
if desc {
|
if desc {
|
||||||
node = sortedSet.skiplist.getLastInScoreRange(min, max)
|
node = sortedSet.skiplist.getLastInRange(min, max)
|
||||||
} else {
|
} else {
|
||||||
node = sortedSet.skiplist.getFirstInScoreRange(min, max)
|
node = sortedSet.skiplist.getFirstInRange(min, max)
|
||||||
}
|
}
|
||||||
|
|
||||||
for node != nil && offset > 0 {
|
for node != nil && offset > 0 {
|
||||||
@@ -180,31 +180,31 @@ func (sortedSet *SortedSet) ForEachByScore(min *ScoreBorder, max *ScoreBorder, o
|
|||||||
if node == nil {
|
if node == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
gtMin := min.less(node.Element.Score) // greater than min
|
gtMin := min.less(&node.Element) // greater than min
|
||||||
ltMax := max.greater(node.Element.Score)
|
ltMax := max.greater(&node.Element)
|
||||||
if !gtMin || !ltMax {
|
if !gtMin || !ltMax {
|
||||||
break // break through score border
|
break // break through score border
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RangeByScore returns members which score within the given border
|
// Range returns members which score or member within the given border
|
||||||
// param limit: <0 means no limit
|
// param limit: <0 means no limit
|
||||||
func (sortedSet *SortedSet) RangeByScore(min *ScoreBorder, max *ScoreBorder, offset int64, limit int64, desc bool) []*Element {
|
func (sortedSet *SortedSet) Range(min Border, max Border, offset int64, limit int64, desc bool) []*Element {
|
||||||
if limit == 0 || offset < 0 {
|
if limit == 0 || offset < 0 {
|
||||||
return make([]*Element, 0)
|
return make([]*Element, 0)
|
||||||
}
|
}
|
||||||
slice := make([]*Element, 0)
|
slice := make([]*Element, 0)
|
||||||
sortedSet.ForEachByScore(min, max, offset, limit, desc, func(element *Element) bool {
|
sortedSet.ForEach(min, max, offset, limit, desc, func(element *Element) bool {
|
||||||
slice = append(slice, element)
|
slice = append(slice, element)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
return slice
|
return slice
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveByScore removes members which score within the given border
|
// RemoveRange removes members which score or member within the given border
|
||||||
func (sortedSet *SortedSet) RemoveByScore(min *ScoreBorder, max *ScoreBorder) int64 {
|
func (sortedSet *SortedSet) RemoveRange(min Border, max Border) int64 {
|
||||||
removed := sortedSet.skiplist.RemoveRangeByScore(min, max, 0)
|
removed := sortedSet.skiplist.RemoveRange(min, max, 0)
|
||||||
for _, element := range removed {
|
for _, element := range removed {
|
||||||
delete(sortedSet.dict, element.Member)
|
delete(sortedSet.dict, element.Member)
|
||||||
}
|
}
|
||||||
@@ -212,7 +212,7 @@ func (sortedSet *SortedSet) RemoveByScore(min *ScoreBorder, max *ScoreBorder) in
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sortedSet *SortedSet) PopMin(count int) []*Element {
|
func (sortedSet *SortedSet) PopMin(count int) []*Element {
|
||||||
first := sortedSet.skiplist.getFirstInScoreRange(negativeInfBorder, positiveInfBorder)
|
first := sortedSet.skiplist.getFirstInRange(scoreNegativeInfBorder, scorePositiveInfBorder)
|
||||||
if first == nil {
|
if first == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -220,7 +220,7 @@ func (sortedSet *SortedSet) PopMin(count int) []*Element {
|
|||||||
Value: first.Score,
|
Value: first.Score,
|
||||||
Exclude: false,
|
Exclude: false,
|
||||||
}
|
}
|
||||||
removed := sortedSet.skiplist.RemoveRangeByScore(border, positiveInfBorder, count)
|
removed := sortedSet.skiplist.RemoveRange(border, scorePositiveInfBorder, count)
|
||||||
for _, element := range removed {
|
for _, element := range removed {
|
||||||
delete(sortedSet.dict, element.Member)
|
delete(sortedSet.dict, element.Member)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user