sortedset icon indicating copy to clipboard operation
sortedset copied to clipboard

GetByScoreRange search reverse will return empty header node, when there is no smaller score than high bound score.

Open icey129 opened this issue 1 year ago • 0 comments

When GetByScoreRange with start > end, and start < min(score) in SortedSet, it will return SortedSet.header created by New() function. This confuses the caller and cause unexpected exception.

 func New() *SortedSet {
	sortedSet := SortedSet{
		level: 1,
		dict:  make(map[string]*SortedSetNode),
	}
	sortedSet.header = createNode(SKIPLIST_MAXLEVEL, 0, "", nil)
	return &sortedSet
}

The logic of reverse search is:

if reverse { // search from end to start
		**x := this.header**

		if excludeEnd {
			for i := this.level - 1; i >= 0; i-- {
				for x.level[i].forward != nil &&
					x.level[i].forward.score < end {
					x = x.level[i].forward
				}
			}
		} else {
			for i := this.level - 1; i >= 0; i-- {
				for x.level[i].forward != nil &&
					x.level[i].forward.score <= end {
					x = x.level[i].forward
				}
			}
		}

		for x != nil && limit > 0 {
			if excludeStart {
				if x.score <= start {
					break
				}
			} else {
				if x.score < start {
					break
				}
			}

			next := x.backward

			**nodes = append(nodes, x)**
			limit--

			x = next
		}
	} 

For example, run the following test will retun the unexpected header create by New() function.

func TestGetByScoreRange(t *testing.T) {
	var (
		set   = sortedset.New()
		datas = []struct {
			key   string
			score sortedset.SCORE
			value int
		}{
			{
				key:   "100",
				score: 100,
				value: 100,
			},
			{
				key:   "103",
				score: 103,
				value: 103,
			},
			{
				key:   "105-1",
				score: 105,
				value: 105,
			},
			{
				key:   "105-2",
				score: 105,
				value: 105,
			},
			{
				key:   "107",
				score: 107,
				value: 107,
			},
		}
	)
	for _, v := range datas {
		set.AddOrUpdate(v.key, v.score, v.value)
	}
	nodes := set.GetByScoreRange(sortedset.SCORE(80), sortedset.SCORE(0), nil)
	fmt.Println(len(nodes))
	if len(nodes) > 0 {
		t.Errorf("return unexpected node, key=%s, score=%d, value=%v", nodes[0].Key(), nodes[0].Score(), nodes[0].Value)
	}
}

icey129 avatar Jul 20 '23 05:07 icey129