github.com/songzhibin97/go-baseutils@v0.0.2-0.20240302024150-487d8ce9c082/structure/sets/zset/zset.go (about)

     1  // Package zset provides a concurrent-safety sorted set, can be used as a local
     2  // replacement of Redis' zset (https://redis.com/ebook/part-2-core-concepts/chapter-3-commands-in-redis/3-5-sorted-sets/).
     3  //
     4  // The main different to other sets is, every value of set is associated with a score,
     5  // that is used in order to take the sorted set ordered, from the smallest to the greatest score.
     6  //
     7  // The sorted set has O(log(N)) time complexity when doing Add(ZADD) and
     8  // Remove(ZREM) operations and O(1) time complexity when doing Contains operations.
     9  package zset
    10  
    11  import (
    12  	"fmt"
    13  	"math"
    14  	"strings"
    15  	"sync"
    16  
    17  	"github.com/songzhibin97/go-baseutils/base/bcomparator"
    18  	"github.com/songzhibin97/go-baseutils/structure/sets"
    19  )
    20  
    21  var _ sets.Set[int] = (*Set[int])(nil)
    22  
    23  // Node represents an element of Set.
    24  type Node[K comparable] struct {
    25  	Value K
    26  	Score float64
    27  }
    28  
    29  // Set is a sorted set implementation with string value and float64 score.
    30  type Set[K comparable] struct {
    31  	mu   sync.RWMutex
    32  	dict map[K]float64
    33  	list *list[K]
    34  }
    35  
    36  func (z *Set[K]) Add(elements ...K) {
    37  	for _, element := range elements {
    38  		z.AddB(0, element)
    39  	}
    40  }
    41  
    42  func (z *Set[K]) Remove(elements ...K) {
    43  	for _, element := range elements {
    44  		z.RemoveB(element)
    45  	}
    46  }
    47  
    48  func (z *Set[K]) Contains(elements ...K) bool {
    49  	for _, element := range elements {
    50  		if !z.ContainsB(element) {
    51  			return false
    52  		}
    53  	}
    54  	return true
    55  }
    56  
    57  func (z *Set[K]) Empty() bool {
    58  	return z.Size() == 0
    59  }
    60  
    61  func (z *Set[K]) Size() int {
    62  	return z.Count(0, math.MaxFloat64)
    63  }
    64  
    65  func (z *Set[K]) Clear() {
    66  	z.dict = make(map[K]float64)
    67  	z.list = newList(z.list.comparator)
    68  }
    69  
    70  func (z *Set[K]) Values() []K {
    71  	list := z.Range(0, -1)
    72  	ret := make([]K, 0, len(list))
    73  	for _, n := range list {
    74  		ret = append(ret, n.Value)
    75  	}
    76  	return ret
    77  }
    78  
    79  func (z *Set[K]) String() string {
    80  	b := strings.Builder{}
    81  	b.WriteString("ZSet\n")
    82  	for _, v := range z.Values() {
    83  		b.WriteString(fmt.Sprintf("(key:%v) ", v))
    84  	}
    85  	return b.String()
    86  }
    87  
    88  // New returns an empty string sorted set with int score.
    89  // strings are sorted in ascending order.
    90  func New[K comparable](comparator bcomparator.Comparator[K]) *Set[K] {
    91  	return &Set[K]{
    92  		dict: make(map[K]float64),
    93  		list: newList(comparator),
    94  	}
    95  }
    96  
    97  // Union returns the union of given sorted sets, the resulting score of
    98  // a value is the sum of its scores in the sorted sets where it exists.
    99  //
   100  // Union is the replacement of UNIONSTORE command of redis.
   101  func Union[K comparable](comparator bcomparator.Comparator[K], zs ...*Set[K]) *Set[K] {
   102  	dest := New(comparator)
   103  	for _, z := range zs {
   104  		for _, n := range z.Range(0, -1) {
   105  			dest.AddB(n.Score, n.Value)
   106  		}
   107  	}
   108  	return dest
   109  }
   110  
   111  // Inter returns the intersection of given sorted sets, the resulting
   112  // score of a value is the sum of its scores in the sorted sets where it exists.
   113  //
   114  // Inter is the replacement of INTERSTORE command of redis.
   115  func Inter[K comparable](comparator bcomparator.Comparator[K], zs ...*Set[K]) *Set[K] {
   116  	dest := New(comparator)
   117  	if len(zs) == 0 {
   118  		return dest
   119  	}
   120  	for _, n := range zs[0].Range(0, -1) {
   121  		ok := true
   122  		for _, z := range zs[1:] {
   123  			if !z.Contains(n.Value) {
   124  				ok = false
   125  				break
   126  			}
   127  		}
   128  		if ok {
   129  			dest.AddB(n.Score, n.Value)
   130  		}
   131  	}
   132  	return dest
   133  }
   134  
   135  // Len returns the length of Set.
   136  //
   137  // Len is the replacement of ZCARD command of redis.
   138  func (z *Set[K]) Len() int {
   139  	z.mu.RLock()
   140  	defer z.mu.RUnlock()
   141  
   142  	return z.list.length
   143  }
   144  
   145  // Add adds a new value or update the score of an existing value.
   146  // Returns true if the value is newly created.
   147  //
   148  // Add is the replacement of ZADD command of redis.
   149  func (z *Set[K]) AddB(score float64, value K) bool {
   150  	z.mu.Lock()
   151  	defer z.mu.Unlock()
   152  
   153  	oldScore, ok := z.dict[value]
   154  	if ok {
   155  		// Update score if need.
   156  		if score != oldScore {
   157  			_ = z.list.UpdateScore(oldScore, value, score)
   158  			z.dict[value] = score
   159  		}
   160  		return false
   161  	}
   162  
   163  	// Insert a new element.
   164  	z.list.Insert(score, value)
   165  	z.dict[value] = score
   166  	return true
   167  }
   168  
   169  // Remove removes a value from the sorted set.
   170  // Returns score of the removed value and true if the node was found and deleted,
   171  // otherwise returns (0.0, false).
   172  //
   173  // Remove is the replacement of ZREM command of redis.
   174  func (z *Set[K]) RemoveB(value K) (float64, bool) {
   175  	z.mu.Lock()
   176  	defer z.mu.Unlock()
   177  
   178  	score, ok := z.dict[value]
   179  	if !ok {
   180  		return 0, false
   181  	}
   182  	delete(z.dict, value)
   183  	z.list.Delete(score, value)
   184  	return score, true
   185  }
   186  
   187  // IncrBy increments the score of value in the sorted set by incr.
   188  // If value does not exist in the sorted set, it is added with incr as its score
   189  // (as if its previous score was zero).
   190  //
   191  // IncrBy is the replacement of ZINCRBY command of redis.
   192  func (z *Set[K]) IncrBy(incr float64, value K) (float64, bool) {
   193  	z.mu.Lock()
   194  	defer z.mu.Unlock()
   195  
   196  	oldScore, ok := z.dict[value]
   197  	if !ok {
   198  		// Insert a new element.
   199  		z.list.Insert(incr, value)
   200  		z.dict[value] = incr
   201  		return incr, false
   202  	}
   203  	// Update score.
   204  	newScore := oldScore + incr
   205  	_ = z.list.UpdateScore(oldScore, value, newScore)
   206  	z.dict[value] = newScore
   207  	return newScore, true
   208  }
   209  
   210  // Contains returns whether the value exists in sorted set.
   211  func (z *Set[K]) ContainsB(value K) bool {
   212  	_, ok := z.Score(value)
   213  	return ok
   214  }
   215  
   216  // Score returns the score of the value in the sorted set.
   217  //
   218  // Score is the replacement of ZSCORE command of redis.
   219  func (z *Set[K]) Score(value K) (float64, bool) {
   220  	z.mu.RLock()
   221  	defer z.mu.RUnlock()
   222  
   223  	score, ok := z.dict[value]
   224  	return score, ok
   225  }
   226  
   227  // Rank returns the rank of element in the sorted set, with the scores
   228  // ordered from low to high.
   229  // The rank (or index) is 0-based, which means that the member with the lowest
   230  // score has rank 0.
   231  // -1 is returned when value is not found.
   232  //
   233  // Rank is the replacement of ZRANK command of redis.
   234  func (z *Set[K]) Rank(value K) int {
   235  	z.mu.RLock()
   236  	defer z.mu.RUnlock()
   237  
   238  	score, ok := z.dict[value]
   239  	if !ok {
   240  		return -1
   241  	}
   242  	// NOTE: list.Rank returns 1-based rank.
   243  	return z.list.Rank(score, value) - 1
   244  }
   245  
   246  // RevRank returns the rank of element in the sorted set, with the scores
   247  // ordered from high to low.
   248  // The rank (or index) is 0-based, which means that the member with the highest
   249  // score has rank 0.
   250  // -1 is returned when value is not found.
   251  //
   252  // RevRank is the replacement of ZREVRANK command of redis.
   253  func (z *Set[K]) RevRank(value K) int {
   254  	z.mu.RLock()
   255  	defer z.mu.RUnlock()
   256  
   257  	score, ok := z.dict[value]
   258  	if !ok {
   259  		return -1
   260  	}
   261  	// NOTE: list.Rank returns 1-based rank.
   262  	return z.list.Rank(score, value) - 1
   263  }
   264  
   265  // Count returns the number of elements in the sorted set at element with a score
   266  // between min and max (including elements with score equal to min or max).
   267  //
   268  // Count is the replacement of ZCOUNT command of redis.
   269  func (z *Set[K]) Count(min, max float64) int {
   270  	return z.CountWithOpt(min, max, RangeOpt{})
   271  }
   272  
   273  func (z *Set[K]) CountWithOpt(min, max float64, opt RangeOpt) int {
   274  	z.mu.RLock()
   275  	defer z.mu.RUnlock()
   276  
   277  	first := z.list.FirstInRange(min, max, opt)
   278  	if first == nil {
   279  		return 0
   280  	}
   281  	// Sub 1 for 1-based rank.
   282  	firstRank := z.list.Rank(first.score, first.value) - 1
   283  	last := z.list.LastInRange(min, max, opt)
   284  	if last == nil {
   285  		return z.list.length - firstRank
   286  	}
   287  	// Sub 1 for 1-based rank.
   288  	lastRank := z.list.Rank(last.score, last.value) - 1
   289  	return lastRank - firstRank + 1
   290  }
   291  
   292  // Range returns the specified inclusive range of elements in the sorted set by rank(index).
   293  // Both start and stop are 0-based, they can also be negative numbers indicating
   294  // offsets from the end of the sorted set, with -1 being the last element of the sorted set,
   295  // and so on.
   296  //
   297  // The returned elements are ordered by score, from lowest to highest.
   298  // Elements with the same score are ordered lexicographically.
   299  //
   300  // This function won't panic even when the given rank out of range.
   301  //
   302  // NOTE: Please always use z.Range(0, -1) for iterating the whole sorted set.
   303  // z.Range(0, z.Len()-1) has 2 method calls, the sorted set may changes during
   304  // the gap of calls.
   305  //
   306  // Range is the replacement of ZRANGE command of redis.
   307  func (z *Set[K]) Range(start, stop int) []Node[K] {
   308  	z.mu.RLock()
   309  	defer z.mu.RUnlock()
   310  
   311  	// Convert negative rank to positive.
   312  	if start < 0 {
   313  		start = z.list.length + start
   314  	}
   315  	if stop < 0 {
   316  		stop = z.list.length + stop
   317  	}
   318  
   319  	var res []Node[K]
   320  	x := z.list.GetNodeByRank(start + 1) // 0-based rank -> 1-based rank
   321  	for x != nil && start <= stop {
   322  		start++
   323  		res = append(res, Node[K]{
   324  			Score: x.score,
   325  			Value: x.value,
   326  		})
   327  		x = x.loadNext(0)
   328  	}
   329  	return res
   330  }
   331  
   332  // RangeByScore returns all the elements in the sorted set with a score
   333  // between min and max (including elements with score equal to min or max).
   334  // The elements are considered to be ordered from low to high scores.
   335  //
   336  // RangeByScore is the replacement of ZRANGEBYSCORE command of redis.
   337  func (z *Set[K]) RangeByScore(min, max float64) []Node[K] {
   338  	return z.RangeByScoreWithOpt(min, max, RangeOpt{})
   339  }
   340  
   341  func (z *Set[K]) RangeByScoreWithOpt(min, max float64, opt RangeOpt) []Node[K] {
   342  	z.mu.RLock()
   343  	defer z.mu.RUnlock()
   344  
   345  	var res []Node[K]
   346  	x := z.list.FirstInRange(min, max, opt)
   347  	for x != nil && (x.score < max || (!opt.ExcludeMax && x.score == max)) {
   348  		res = append(res, Node[K]{
   349  			Score: x.score,
   350  			Value: x.value,
   351  		})
   352  		x = x.loadNext(0)
   353  	}
   354  	return res
   355  }
   356  
   357  // RevRange returns the specified inclusive range of elements in the sorted set by rank(index).
   358  // Both start and stop are 0-based, they can also be negative numbers indicating
   359  // offsets from the end of the sorted set, with -1 being the first element of the sorted set,
   360  // and so on.
   361  //
   362  // The returned elements are ordered by score, from highest to lowest.
   363  // Elements with the same score are ordered in reverse lexicographical ordering.
   364  //
   365  // This function won't panic even when the given rank out of range.
   366  //
   367  // NOTE: Please always use z.RevRange(0, -1) for iterating the whole sorted set.
   368  // z.RevRange(0, z.Len()-1) has 2 method calls, the sorted set may changes during
   369  // the gap of calls.
   370  //
   371  // RevRange is the replacement of ZREVRANGE command of redis.
   372  func (z *Set[K]) RevRange(start, stop int) []Node[K] {
   373  	z.mu.RLock()
   374  	defer z.mu.RUnlock()
   375  
   376  	// Convert negative rank to positive.
   377  	if start < 0 {
   378  		start = z.list.length + start
   379  	}
   380  	if stop < 0 {
   381  		stop = z.list.length + stop
   382  	}
   383  
   384  	var res []Node[K]
   385  	x := z.list.GetNodeByRank(z.list.length - start) // 0-based rank -> 1-based rank
   386  	for x != nil && start <= stop {
   387  		start++
   388  		res = append(res, Node[K]{
   389  			Score: x.score,
   390  			Value: x.value,
   391  		})
   392  		x = x.prev
   393  	}
   394  	return res
   395  }
   396  
   397  // RevRangeByScore returns all the elements in the sorted set with a
   398  // score between max and min (including elements with score equal to max or min).
   399  // The elements are considered to be ordered from high to low scores.
   400  //
   401  // RevRangeByScore is the replacement of ZREVRANGEBYSCORE command of redis.
   402  func (z *Set[K]) RevRangeByScore(max, min float64) []Node[K] {
   403  	return z.RevRangeByScoreWithOpt(max, min, RangeOpt{})
   404  }
   405  
   406  func (z *Set[K]) RevRangeByScoreWithOpt(max, min float64, opt RangeOpt) []Node[K] {
   407  	z.mu.RLock()
   408  	defer z.mu.RUnlock()
   409  
   410  	var res []Node[K]
   411  	x := z.list.LastInRange(min, max, opt)
   412  	for x != nil && (x.score > min || (!opt.ExcludeMin && x.score == min)) {
   413  		res = append(res, Node[K]{
   414  			Score: x.score,
   415  			Value: x.value,
   416  		})
   417  		x = x.prev
   418  	}
   419  	return res
   420  }
   421  
   422  // RemoveRangeByRank removes all elements in the sorted set stored with rank
   423  // between start and stop.
   424  // Both start and stop are 0-based, they can also be negative numbers indicating
   425  // offsets from the end of the sorted set, with -1 being the last element of the sorted set,
   426  // and so on.
   427  //
   428  // RemoveRangeByRank is the replacement of ZREMRANGEBYRANK command of redis.
   429  func (z *Set[K]) RemoveRangeByRank(start, stop int) []Node[K] {
   430  	z.mu.RLock()
   431  	defer z.mu.RUnlock()
   432  
   433  	// Convert negative rank to positive.
   434  	if start < 0 {
   435  		start = z.list.length + start
   436  	}
   437  	if stop < 0 {
   438  		stop = z.list.length + stop
   439  	}
   440  
   441  	return z.list.DeleteRangeByRank(start+1, stop+1, z.dict) // 0-based rank -> 1-based rank
   442  }
   443  
   444  // RemoveRangeByScore removes all elements in the sorted set stored with a score
   445  // between min and max (including elements with score equal to min or max).
   446  //
   447  // RemoveRangeByScore is the replacement of ZREMRANGEBYSCORE command of redis.
   448  func (z *Set[K]) RemoveRangeByScore(min, max float64) []Node[K] {
   449  	return z.RevRangeByScoreWithOpt(min, max, RangeOpt{})
   450  }
   451  
   452  func (z *Set[K]) RemoveRangeByScoreWithOpt(min, max float64, opt RangeOpt) []Node[K] {
   453  	z.mu.RLock()
   454  	defer z.mu.RUnlock()
   455  
   456  	return z.list.DeleteRangeByScore(min, max, opt, z.dict)
   457  }