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:
@@ -14,10 +14,20 @@ import (
|
||||
*/
|
||||
|
||||
const (
|
||||
negativeInf int8 = -1
|
||||
positiveInf int8 = 1
|
||||
scoreNegativeInf 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
|
||||
type ScoreBorder struct {
|
||||
Inf int8
|
||||
@@ -27,10 +37,11 @@ type ScoreBorder struct {
|
||||
|
||||
// if max.greater(score) then the score is within the upper border
|
||||
// do not use min.greater()
|
||||
func (border *ScoreBorder) greater(value float64) bool {
|
||||
if border.Inf == negativeInf {
|
||||
func (border *ScoreBorder) greater(element *Element) bool {
|
||||
value := element.Score
|
||||
if border.Inf == scoreNegativeInf {
|
||||
return false
|
||||
} else if border.Inf == positiveInf {
|
||||
} else if border.Inf == scorePositiveInf {
|
||||
return true
|
||||
}
|
||||
if border.Exclude {
|
||||
@@ -39,10 +50,11 @@ func (border *ScoreBorder) greater(value float64) bool {
|
||||
return border.Value >= value
|
||||
}
|
||||
|
||||
func (border *ScoreBorder) less(value float64) bool {
|
||||
if border.Inf == negativeInf {
|
||||
func (border *ScoreBorder) less(element *Element) bool {
|
||||
value := element.Score
|
||||
if border.Inf == scoreNegativeInf {
|
||||
return true
|
||||
} else if border.Inf == positiveInf {
|
||||
} else if border.Inf == scorePositiveInf {
|
||||
return false
|
||||
}
|
||||
if border.Exclude {
|
||||
@@ -51,21 +63,29 @@ func (border *ScoreBorder) less(value float64) bool {
|
||||
return border.Value <= value
|
||||
}
|
||||
|
||||
var positiveInfBorder = &ScoreBorder{
|
||||
Inf: positiveInf,
|
||||
func (border *ScoreBorder) getValue() interface{} {
|
||||
return border.Value
|
||||
}
|
||||
|
||||
var negativeInfBorder = &ScoreBorder{
|
||||
Inf: negativeInf,
|
||||
func (border *ScoreBorder) getExclude() bool {
|
||||
return border.Exclude
|
||||
}
|
||||
|
||||
var scorePositiveInfBorder = &ScoreBorder{
|
||||
Inf: scorePositiveInf,
|
||||
}
|
||||
|
||||
var scoreNegativeInfBorder = &ScoreBorder{
|
||||
Inf: scoreNegativeInf,
|
||||
}
|
||||
|
||||
// ParseScoreBorder creates ScoreBorder from redis arguments
|
||||
func ParseScoreBorder(s string) (*ScoreBorder, error) {
|
||||
func ParseScoreBorder(s string) (Border, error) {
|
||||
if s == "inf" || s == "+inf" {
|
||||
return positiveInfBorder, nil
|
||||
return scorePositiveInfBorder, nil
|
||||
}
|
||||
if s == "-inf" {
|
||||
return negativeInfBorder, nil
|
||||
return scoreNegativeInfBorder, nil
|
||||
}
|
||||
if s[0] == '(' {
|
||||
value, err := strconv.ParseFloat(s[1:], 64)
|
||||
@@ -88,3 +108,93 @@ func ParseScoreBorder(s string) (*ScoreBorder, error) {
|
||||
Exclude: false,
|
||||
}, 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
|
||||
}
|
||||
|
||||
func (skiplist *skiplist) hasInRange(min *ScoreBorder, max *ScoreBorder) bool {
|
||||
// min & max = empty
|
||||
if min.Value > max.Value || (min.Value == max.Value && (min.Exclude || max.Exclude)) {
|
||||
func (skiplist *skiplist) hasInRange(min Border, max Border) bool {
|
||||
if min.isIntersected(max) { //是有交集的,则返回false
|
||||
return false
|
||||
}
|
||||
|
||||
// min > tail
|
||||
n := skiplist.tail
|
||||
if n == nil || !min.less(n.Score) {
|
||||
if n == nil || !min.less(&n.Element) {
|
||||
return false
|
||||
}
|
||||
// max < head
|
||||
n = skiplist.header.level[0].forward
|
||||
if n == nil || !max.greater(n.Score) {
|
||||
if n == nil || !max.greater(&n.Element) {
|
||||
return false
|
||||
}
|
||||
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) {
|
||||
return nil
|
||||
}
|
||||
@@ -247,30 +247,30 @@ func (skiplist *skiplist) getFirstInScoreRange(min *ScoreBorder, max *ScoreBorde
|
||||
// scan from top level
|
||||
for level := skiplist.level - 1; level >= 0; level-- {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
/* This is an inner range, so the next node cannot be NULL. */
|
||||
n = n.level[0].forward
|
||||
if !max.greater(n.Score) {
|
||||
if !max.greater(&n.Element) {
|
||||
return nil
|
||||
}
|
||||
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) {
|
||||
return nil
|
||||
}
|
||||
n := skiplist.header
|
||||
// scan from top 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
|
||||
}
|
||||
}
|
||||
if !min.less(n.Score) {
|
||||
if !min.less(&n.Element) {
|
||||
return nil
|
||||
}
|
||||
return n
|
||||
@@ -279,14 +279,14 @@ func (skiplist *skiplist) getLastInScoreRange(min *ScoreBorder, max *ScoreBorder
|
||||
/*
|
||||
* 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)
|
||||
removed = make([]*Element, 0)
|
||||
// find backward nodes (of target range) or last node of each level
|
||||
node := skiplist.header
|
||||
for i := skiplist.level - 1; i >= 0; i-- {
|
||||
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
|
||||
}
|
||||
node = node.level[i].forward
|
||||
@@ -299,7 +299,7 @@ func (skiplist *skiplist) RemoveRangeByScore(min *ScoreBorder, max *ScoreBorder,
|
||||
|
||||
// remove nodes in range
|
||||
for node != nil {
|
||||
if !max.greater(node.Score) { // already out of range
|
||||
if !max.greater(&node.Element) { // already out of range
|
||||
break
|
||||
}
|
||||
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 {
|
||||
element, ok := sortedSet.dict[member]
|
||||
sortedSet.dict[member] = &Element{
|
||||
@@ -76,8 +76,8 @@ func (sortedSet *SortedSet) GetRank(member string, desc bool) (rank int64) {
|
||||
return r
|
||||
}
|
||||
|
||||
// ForEach 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) {
|
||||
// ForEachByRank visits each member which rank within [start, stop), sort by ascending order, rank starts from 0
|
||||
func (sortedSet *SortedSet) ForEachByRank(start int64, stop int64, desc bool, consumer func(element *Element) bool) {
|
||||
size := int64(sortedSet.Len())
|
||||
if start < 0 || start >= size {
|
||||
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
|
||||
func (sortedSet *SortedSet) Range(start int64, stop int64, desc bool) []*Element {
|
||||
// RangeByRank returns members which rank within [start, stop), sort by ascending order, rank starts from 0
|
||||
func (sortedSet *SortedSet) RangeByRank(start int64, stop int64, desc bool) []*Element {
|
||||
sliceSize := int(stop - start)
|
||||
slice := make([]*Element, sliceSize)
|
||||
i := 0
|
||||
sortedSet.ForEach(start, stop, desc, func(element *Element) bool {
|
||||
sortedSet.ForEachByRank(start, stop, desc, func(element *Element) bool {
|
||||
slice[i] = element
|
||||
i++
|
||||
return true
|
||||
@@ -126,17 +126,17 @@ func (sortedSet *SortedSet) Range(start int64, stop int64, desc bool) []*Element
|
||||
return slice
|
||||
}
|
||||
|
||||
// Count returns the number of members which score within the given border
|
||||
func (sortedSet *SortedSet) Count(min *ScoreBorder, max *ScoreBorder) int64 {
|
||||
// RangeCount returns the number of members which score or member within the given border
|
||||
func (sortedSet *SortedSet) RangeCount(min Border, max Border) int64 {
|
||||
var i int64 = 0
|
||||
// ascending order
|
||||
sortedSet.ForEach(0, sortedSet.Len(), false, func(element *Element) bool {
|
||||
gtMin := min.less(element.Score) // greater than min
|
||||
sortedSet.ForEachByRank(0, sortedSet.Len(), false, func(element *Element) bool {
|
||||
gtMin := min.less(element) // greater than min
|
||||
if !gtMin {
|
||||
// has not into range, continue foreach
|
||||
return true
|
||||
}
|
||||
ltMax := max.greater(element.Score) // less than max
|
||||
ltMax := max.greater(element) // less than max
|
||||
if !ltMax {
|
||||
// break through score border, break foreach
|
||||
return false
|
||||
@@ -148,14 +148,14 @@ func (sortedSet *SortedSet) Count(min *ScoreBorder, max *ScoreBorder) int64 {
|
||||
return i
|
||||
}
|
||||
|
||||
// ForEachByScore visits members which score within the given border
|
||||
func (sortedSet *SortedSet) ForEachByScore(min *ScoreBorder, max *ScoreBorder, offset int64, limit int64, desc bool, consumer func(element *Element) bool) {
|
||||
// ForEach visits members which score or member within the given border
|
||||
func (sortedSet *SortedSet) ForEach(min Border, max Border, offset int64, limit int64, desc bool, consumer func(element *Element) bool) {
|
||||
// find start node
|
||||
var node *node
|
||||
if desc {
|
||||
node = sortedSet.skiplist.getLastInScoreRange(min, max)
|
||||
node = sortedSet.skiplist.getLastInRange(min, max)
|
||||
} else {
|
||||
node = sortedSet.skiplist.getFirstInScoreRange(min, max)
|
||||
node = sortedSet.skiplist.getFirstInRange(min, max)
|
||||
}
|
||||
|
||||
for node != nil && offset > 0 {
|
||||
@@ -180,31 +180,31 @@ func (sortedSet *SortedSet) ForEachByScore(min *ScoreBorder, max *ScoreBorder, o
|
||||
if node == nil {
|
||||
break
|
||||
}
|
||||
gtMin := min.less(node.Element.Score) // greater than min
|
||||
ltMax := max.greater(node.Element.Score)
|
||||
gtMin := min.less(&node.Element) // greater than min
|
||||
ltMax := max.greater(&node.Element)
|
||||
if !gtMin || !ltMax {
|
||||
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
|
||||
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 {
|
||||
return 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)
|
||||
return true
|
||||
})
|
||||
return slice
|
||||
}
|
||||
|
||||
// RemoveByScore removes members which score within the given border
|
||||
func (sortedSet *SortedSet) RemoveByScore(min *ScoreBorder, max *ScoreBorder) int64 {
|
||||
removed := sortedSet.skiplist.RemoveRangeByScore(min, max, 0)
|
||||
// RemoveRange removes members which score or member within the given border
|
||||
func (sortedSet *SortedSet) RemoveRange(min Border, max Border) int64 {
|
||||
removed := sortedSet.skiplist.RemoveRange(min, max, 0)
|
||||
for _, element := range removed {
|
||||
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 {
|
||||
first := sortedSet.skiplist.getFirstInScoreRange(negativeInfBorder, positiveInfBorder)
|
||||
first := sortedSet.skiplist.getFirstInRange(scoreNegativeInfBorder, scorePositiveInfBorder)
|
||||
if first == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -220,7 +220,7 @@ func (sortedSet *SortedSet) PopMin(count int) []*Element {
|
||||
Value: first.Score,
|
||||
Exclude: false,
|
||||
}
|
||||
removed := sortedSet.skiplist.RemoveRangeByScore(border, positiveInfBorder, count)
|
||||
removed := sortedSet.skiplist.RemoveRange(border, scorePositiveInfBorder, count)
|
||||
for _, element := range removed {
|
||||
delete(sortedSet.dict, element.Member)
|
||||
}
|
||||
|
Reference in New Issue
Block a user