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 }