github.com/hdt3213/godis@v1.2.9/database/sortedset.go (about) 1 package database 2 3 import ( 4 SortedSet "github.com/hdt3213/godis/datastruct/sortedset" 5 "github.com/hdt3213/godis/interface/database" 6 "github.com/hdt3213/godis/interface/redis" 7 "github.com/hdt3213/godis/lib/utils" 8 "github.com/hdt3213/godis/redis/protocol" 9 "strconv" 10 "strings" 11 ) 12 13 func (db *DB) getAsSortedSet(key string) (*SortedSet.SortedSet, protocol.ErrorReply) { 14 entity, exists := db.GetEntity(key) 15 if !exists { 16 return nil, nil 17 } 18 sortedSet, ok := entity.Data.(*SortedSet.SortedSet) 19 if !ok { 20 return nil, &protocol.WrongTypeErrReply{} 21 } 22 return sortedSet, nil 23 } 24 25 func (db *DB) getOrInitSortedSet(key string) (sortedSet *SortedSet.SortedSet, inited bool, errReply protocol.ErrorReply) { 26 sortedSet, errReply = db.getAsSortedSet(key) 27 if errReply != nil { 28 return nil, false, errReply 29 } 30 inited = false 31 if sortedSet == nil { 32 sortedSet = SortedSet.Make() 33 db.PutEntity(key, &database.DataEntity{ 34 Data: sortedSet, 35 }) 36 inited = true 37 } 38 return sortedSet, inited, nil 39 } 40 41 // execZAdd adds member into sorted set 42 func execZAdd(db *DB, args [][]byte) redis.Reply { 43 if len(args)%2 != 1 { 44 return protocol.MakeSyntaxErrReply() 45 } 46 key := string(args[0]) 47 size := (len(args) - 1) / 2 48 elements := make([]*SortedSet.Element, size) 49 for i := 0; i < size; i++ { 50 scoreValue := args[2*i+1] 51 member := string(args[2*i+2]) 52 score, err := strconv.ParseFloat(string(scoreValue), 64) 53 if err != nil { 54 return protocol.MakeErrReply("ERR value is not a valid float") 55 } 56 elements[i] = &SortedSet.Element{ 57 Member: member, 58 Score: score, 59 } 60 } 61 62 // get or init entity 63 sortedSet, _, errReply := db.getOrInitSortedSet(key) 64 if errReply != nil { 65 return errReply 66 } 67 68 i := 0 69 for _, e := range elements { 70 if sortedSet.Add(e.Member, e.Score) { 71 i++ 72 } 73 } 74 75 db.addAof(utils.ToCmdLine3("zadd", args...)) 76 77 return protocol.MakeIntReply(int64(i)) 78 } 79 80 func undoZAdd(db *DB, args [][]byte) []CmdLine { 81 key := string(args[0]) 82 size := (len(args) - 1) / 2 83 fields := make([]string, size) 84 for i := 0; i < size; i++ { 85 fields[i] = string(args[2*i+2]) 86 } 87 return rollbackZSetFields(db, key, fields...) 88 } 89 90 // execZScore gets score of a member in sortedset 91 func execZScore(db *DB, args [][]byte) redis.Reply { 92 // parse args 93 key := string(args[0]) 94 member := string(args[1]) 95 96 sortedSet, errReply := db.getAsSortedSet(key) 97 if errReply != nil { 98 return errReply 99 } 100 if sortedSet == nil { 101 return &protocol.NullBulkReply{} 102 } 103 104 element, exists := sortedSet.Get(member) 105 if !exists { 106 return &protocol.NullBulkReply{} 107 } 108 value := strconv.FormatFloat(element.Score, 'f', -1, 64) 109 return protocol.MakeBulkReply([]byte(value)) 110 } 111 112 // execZRank gets index of a member in sortedset, ascending order, start from 0 113 func execZRank(db *DB, args [][]byte) redis.Reply { 114 // parse args 115 key := string(args[0]) 116 member := string(args[1]) 117 118 // get entity 119 sortedSet, errReply := db.getAsSortedSet(key) 120 if errReply != nil { 121 return errReply 122 } 123 if sortedSet == nil { 124 return &protocol.NullBulkReply{} 125 } 126 127 rank := sortedSet.GetRank(member, false) 128 if rank < 0 { 129 return &protocol.NullBulkReply{} 130 } 131 return protocol.MakeIntReply(rank) 132 } 133 134 // execZRevRank gets index of a member in sortedset, descending order, start from 0 135 func execZRevRank(db *DB, args [][]byte) redis.Reply { 136 // parse args 137 key := string(args[0]) 138 member := string(args[1]) 139 140 // get entity 141 sortedSet, errReply := db.getAsSortedSet(key) 142 if errReply != nil { 143 return errReply 144 } 145 if sortedSet == nil { 146 return &protocol.NullBulkReply{} 147 } 148 149 rank := sortedSet.GetRank(member, true) 150 if rank < 0 { 151 return &protocol.NullBulkReply{} 152 } 153 return protocol.MakeIntReply(rank) 154 } 155 156 // execZCard gets number of members in sortedset 157 func execZCard(db *DB, args [][]byte) redis.Reply { 158 // parse args 159 key := string(args[0]) 160 161 // get entity 162 sortedSet, errReply := db.getAsSortedSet(key) 163 if errReply != nil { 164 return errReply 165 } 166 if sortedSet == nil { 167 return protocol.MakeIntReply(0) 168 } 169 170 return protocol.MakeIntReply(sortedSet.Len()) 171 } 172 173 // execZRange gets members in range, sort by score in ascending order 174 func execZRange(db *DB, args [][]byte) redis.Reply { 175 // parse args 176 if len(args) != 3 && len(args) != 4 { 177 return protocol.MakeErrReply("ERR wrong number of arguments for 'zrange' command") 178 } 179 withScores := false 180 if len(args) == 4 { 181 if strings.ToUpper(string(args[3])) != "WITHSCORES" { 182 return protocol.MakeErrReply("syntax error") 183 } 184 withScores = true 185 } 186 key := string(args[0]) 187 start, err := strconv.ParseInt(string(args[1]), 10, 64) 188 if err != nil { 189 return protocol.MakeErrReply("ERR value is not an integer or out of range") 190 } 191 stop, err := strconv.ParseInt(string(args[2]), 10, 64) 192 if err != nil { 193 return protocol.MakeErrReply("ERR value is not an integer or out of range") 194 } 195 return range0(db, key, start, stop, withScores, false) 196 } 197 198 // execZRevRange gets members in range, sort by score in descending order 199 func execZRevRange(db *DB, args [][]byte) redis.Reply { 200 // parse args 201 if len(args) != 3 && len(args) != 4 { 202 return protocol.MakeErrReply("ERR wrong number of arguments for 'zrevrange' command") 203 } 204 withScores := false 205 if len(args) == 4 { 206 if string(args[3]) != "WITHSCORES" { 207 return protocol.MakeErrReply("syntax error") 208 } 209 withScores = true 210 } 211 key := string(args[0]) 212 start, err := strconv.ParseInt(string(args[1]), 10, 64) 213 if err != nil { 214 return protocol.MakeErrReply("ERR value is not an integer or out of range") 215 } 216 stop, err := strconv.ParseInt(string(args[2]), 10, 64) 217 if err != nil { 218 return protocol.MakeErrReply("ERR value is not an integer or out of range") 219 } 220 return range0(db, key, start, stop, withScores, true) 221 } 222 223 func range0(db *DB, key string, start int64, stop int64, withScores bool, desc bool) redis.Reply { 224 // get data 225 sortedSet, errReply := db.getAsSortedSet(key) 226 if errReply != nil { 227 return errReply 228 } 229 if sortedSet == nil { 230 return &protocol.EmptyMultiBulkReply{} 231 } 232 233 // compute index 234 size := sortedSet.Len() // assert: size > 0 235 if start < -1*size { 236 start = 0 237 } else if start < 0 { 238 start = size + start 239 } else if start >= size { 240 return &protocol.EmptyMultiBulkReply{} 241 } 242 if stop < -1*size { 243 stop = 0 244 } else if stop < 0 { 245 stop = size + stop + 1 246 } else if stop < size { 247 stop = stop + 1 248 } else { 249 stop = size 250 } 251 if stop < start { 252 stop = start 253 } 254 255 // assert: start in [0, size - 1], stop in [start, size] 256 slice := sortedSet.Range(start, stop, desc) 257 if withScores { 258 result := make([][]byte, len(slice)*2) 259 i := 0 260 for _, element := range slice { 261 result[i] = []byte(element.Member) 262 i++ 263 scoreStr := strconv.FormatFloat(element.Score, 'f', -1, 64) 264 result[i] = []byte(scoreStr) 265 i++ 266 } 267 return protocol.MakeMultiBulkReply(result) 268 } 269 result := make([][]byte, len(slice)) 270 i := 0 271 for _, element := range slice { 272 result[i] = []byte(element.Member) 273 i++ 274 } 275 return protocol.MakeMultiBulkReply(result) 276 } 277 278 // execZCount gets number of members which score within given range 279 func execZCount(db *DB, args [][]byte) redis.Reply { 280 key := string(args[0]) 281 282 min, err := SortedSet.ParseScoreBorder(string(args[1])) 283 if err != nil { 284 return protocol.MakeErrReply(err.Error()) 285 } 286 287 max, err := SortedSet.ParseScoreBorder(string(args[2])) 288 if err != nil { 289 return protocol.MakeErrReply(err.Error()) 290 } 291 292 // get data 293 sortedSet, errReply := db.getAsSortedSet(key) 294 if errReply != nil { 295 return errReply 296 } 297 if sortedSet == nil { 298 return protocol.MakeIntReply(0) 299 } 300 301 return protocol.MakeIntReply(sortedSet.Count(min, max)) 302 } 303 304 /* 305 * param limit: limit < 0 means no limit 306 */ 307 func rangeByScore0(db *DB, key string, min *SortedSet.ScoreBorder, max *SortedSet.ScoreBorder, offset int64, limit int64, withScores bool, desc bool) redis.Reply { 308 // get data 309 sortedSet, errReply := db.getAsSortedSet(key) 310 if errReply != nil { 311 return errReply 312 } 313 if sortedSet == nil { 314 return &protocol.EmptyMultiBulkReply{} 315 } 316 317 slice := sortedSet.RangeByScore(min, max, offset, limit, desc) 318 if withScores { 319 result := make([][]byte, len(slice)*2) 320 i := 0 321 for _, element := range slice { 322 result[i] = []byte(element.Member) 323 i++ 324 scoreStr := strconv.FormatFloat(element.Score, 'f', -1, 64) 325 result[i] = []byte(scoreStr) 326 i++ 327 } 328 return protocol.MakeMultiBulkReply(result) 329 } 330 result := make([][]byte, len(slice)) 331 i := 0 332 for _, element := range slice { 333 result[i] = []byte(element.Member) 334 i++ 335 } 336 return protocol.MakeMultiBulkReply(result) 337 } 338 339 // execZRangeByScore gets members which score within given range, in ascending order 340 func execZRangeByScore(db *DB, args [][]byte) redis.Reply { 341 if len(args) < 3 { 342 return protocol.MakeErrReply("ERR wrong number of arguments for 'zrangebyscore' command") 343 } 344 key := string(args[0]) 345 346 min, err := SortedSet.ParseScoreBorder(string(args[1])) 347 if err != nil { 348 return protocol.MakeErrReply(err.Error()) 349 } 350 351 max, err := SortedSet.ParseScoreBorder(string(args[2])) 352 if err != nil { 353 return protocol.MakeErrReply(err.Error()) 354 } 355 356 withScores := false 357 var offset int64 = 0 358 var limit int64 = -1 359 if len(args) > 3 { 360 for i := 3; i < len(args); { 361 s := string(args[i]) 362 if strings.ToUpper(s) == "WITHSCORES" { 363 withScores = true 364 i++ 365 } else if strings.ToUpper(s) == "LIMIT" { 366 if len(args) < i+3 { 367 return protocol.MakeErrReply("ERR syntax error") 368 } 369 offset, err = strconv.ParseInt(string(args[i+1]), 10, 64) 370 if err != nil { 371 return protocol.MakeErrReply("ERR value is not an integer or out of range") 372 } 373 limit, err = strconv.ParseInt(string(args[i+2]), 10, 64) 374 if err != nil { 375 return protocol.MakeErrReply("ERR value is not an integer or out of range") 376 } 377 i += 3 378 } else { 379 return protocol.MakeErrReply("ERR syntax error") 380 } 381 } 382 } 383 return rangeByScore0(db, key, min, max, offset, limit, withScores, false) 384 } 385 386 // execZRevRangeByScore gets number of members which score within given range, in descending order 387 func execZRevRangeByScore(db *DB, args [][]byte) redis.Reply { 388 if len(args) < 3 { 389 return protocol.MakeErrReply("ERR wrong number of arguments for 'zrangebyscore' command") 390 } 391 key := string(args[0]) 392 393 min, err := SortedSet.ParseScoreBorder(string(args[2])) 394 if err != nil { 395 return protocol.MakeErrReply(err.Error()) 396 } 397 398 max, err := SortedSet.ParseScoreBorder(string(args[1])) 399 if err != nil { 400 return protocol.MakeErrReply(err.Error()) 401 } 402 403 withScores := false 404 var offset int64 = 0 405 var limit int64 = -1 406 if len(args) > 3 { 407 for i := 3; i < len(args); { 408 s := string(args[i]) 409 if strings.ToUpper(s) == "WITHSCORES" { 410 withScores = true 411 i++ 412 } else if strings.ToUpper(s) == "LIMIT" { 413 if len(args) < i+3 { 414 return protocol.MakeErrReply("ERR syntax error") 415 } 416 offset, err = strconv.ParseInt(string(args[i+1]), 10, 64) 417 if err != nil { 418 return protocol.MakeErrReply("ERR value is not an integer or out of range") 419 } 420 limit, err = strconv.ParseInt(string(args[i+2]), 10, 64) 421 if err != nil { 422 return protocol.MakeErrReply("ERR value is not an integer or out of range") 423 } 424 i += 3 425 } else { 426 return protocol.MakeErrReply("ERR syntax error") 427 } 428 } 429 } 430 return rangeByScore0(db, key, min, max, offset, limit, withScores, true) 431 } 432 433 // execZRemRangeByScore removes members which score within given range 434 func execZRemRangeByScore(db *DB, args [][]byte) redis.Reply { 435 if len(args) != 3 { 436 return protocol.MakeErrReply("ERR wrong number of arguments for 'zremrangebyscore' command") 437 } 438 key := string(args[0]) 439 440 min, err := SortedSet.ParseScoreBorder(string(args[1])) 441 if err != nil { 442 return protocol.MakeErrReply(err.Error()) 443 } 444 445 max, err := SortedSet.ParseScoreBorder(string(args[2])) 446 if err != nil { 447 return protocol.MakeErrReply(err.Error()) 448 } 449 450 // get data 451 sortedSet, errReply := db.getAsSortedSet(key) 452 if errReply != nil { 453 return errReply 454 } 455 if sortedSet == nil { 456 return &protocol.EmptyMultiBulkReply{} 457 } 458 459 removed := sortedSet.RemoveByScore(min, max) 460 if removed > 0 { 461 db.addAof(utils.ToCmdLine3("zremrangebyscore", args...)) 462 } 463 return protocol.MakeIntReply(removed) 464 } 465 466 // execZRemRangeByRank removes members within given indexes 467 func execZRemRangeByRank(db *DB, args [][]byte) redis.Reply { 468 key := string(args[0]) 469 start, err := strconv.ParseInt(string(args[1]), 10, 64) 470 if err != nil { 471 return protocol.MakeErrReply("ERR value is not an integer or out of range") 472 } 473 stop, err := strconv.ParseInt(string(args[2]), 10, 64) 474 if err != nil { 475 return protocol.MakeErrReply("ERR value is not an integer or out of range") 476 } 477 478 // get data 479 sortedSet, errReply := db.getAsSortedSet(key) 480 if errReply != nil { 481 return errReply 482 } 483 if sortedSet == nil { 484 return protocol.MakeIntReply(0) 485 } 486 487 // compute index 488 size := sortedSet.Len() // assert: size > 0 489 if start < -1*size { 490 start = 0 491 } else if start < 0 { 492 start = size + start 493 } else if start >= size { 494 return protocol.MakeIntReply(0) 495 } 496 if stop < -1*size { 497 stop = 0 498 } else if stop < 0 { 499 stop = size + stop + 1 500 } else if stop < size { 501 stop = stop + 1 502 } else { 503 stop = size 504 } 505 if stop < start { 506 stop = start 507 } 508 509 // assert: start in [0, size - 1], stop in [start, size] 510 removed := sortedSet.RemoveByRank(start, stop) 511 if removed > 0 { 512 db.addAof(utils.ToCmdLine3("zremrangebyrank", args...)) 513 } 514 return protocol.MakeIntReply(removed) 515 } 516 517 func execZPopMin(db *DB, args [][]byte) redis.Reply { 518 key := string(args[0]) 519 count := 1 520 if len(args) > 1 { 521 var err error 522 count, err = strconv.Atoi(string(args[1])) 523 if err != nil { 524 return protocol.MakeErrReply("ERR value is not an integer or out of range") 525 } 526 } 527 528 sortedSet, errReply := db.getAsSortedSet(key) 529 if errReply != nil { 530 return errReply 531 } 532 if sortedSet == nil { 533 return protocol.MakeEmptyMultiBulkReply() 534 } 535 536 removed := sortedSet.PopMin(count) 537 if len(removed) > 0 { 538 db.addAof(utils.ToCmdLine3("zpopmin", args...)) 539 } 540 result := make([][]byte, 0, len(removed)*2) 541 for _, element := range removed { 542 scoreStr := strconv.FormatFloat(element.Score, 'f', -1, 64) 543 result = append(result, []byte(element.Member), []byte(scoreStr)) 544 } 545 return protocol.MakeMultiBulkReply(result) 546 } 547 548 // execZRem removes given members 549 func execZRem(db *DB, args [][]byte) redis.Reply { 550 // parse args 551 key := string(args[0]) 552 fields := make([]string, len(args)-1) 553 fieldArgs := args[1:] 554 for i, v := range fieldArgs { 555 fields[i] = string(v) 556 } 557 558 // get entity 559 sortedSet, errReply := db.getAsSortedSet(key) 560 if errReply != nil { 561 return errReply 562 } 563 if sortedSet == nil { 564 return protocol.MakeIntReply(0) 565 } 566 567 var deleted int64 = 0 568 for _, field := range fields { 569 if sortedSet.Remove(field) { 570 deleted++ 571 } 572 } 573 if deleted > 0 { 574 db.addAof(utils.ToCmdLine3("zrem", args...)) 575 } 576 return protocol.MakeIntReply(deleted) 577 } 578 579 func undoZRem(db *DB, args [][]byte) []CmdLine { 580 key := string(args[0]) 581 fields := make([]string, len(args)-1) 582 fieldArgs := args[1:] 583 for i, v := range fieldArgs { 584 fields[i] = string(v) 585 } 586 return rollbackZSetFields(db, key, fields...) 587 } 588 589 // execZIncrBy increments the score of a member 590 func execZIncrBy(db *DB, args [][]byte) redis.Reply { 591 key := string(args[0]) 592 rawDelta := string(args[1]) 593 field := string(args[2]) 594 delta, err := strconv.ParseFloat(rawDelta, 64) 595 if err != nil { 596 return protocol.MakeErrReply("ERR value is not a valid float") 597 } 598 599 // get or init entity 600 sortedSet, _, errReply := db.getOrInitSortedSet(key) 601 if errReply != nil { 602 return errReply 603 } 604 605 element, exists := sortedSet.Get(field) 606 if !exists { 607 sortedSet.Add(field, delta) 608 db.addAof(utils.ToCmdLine3("zincrby", args...)) 609 return protocol.MakeBulkReply(args[1]) 610 } 611 score := element.Score + delta 612 sortedSet.Add(field, score) 613 bytes := []byte(strconv.FormatFloat(score, 'f', -1, 64)) 614 db.addAof(utils.ToCmdLine3("zincrby", args...)) 615 return protocol.MakeBulkReply(bytes) 616 } 617 618 func undoZIncr(db *DB, args [][]byte) []CmdLine { 619 key := string(args[0]) 620 field := string(args[2]) 621 return rollbackZSetFields(db, key, field) 622 } 623 624 func init() { 625 registerCommand("ZAdd", execZAdd, writeFirstKey, undoZAdd, -4, flagWrite). 626 attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM, redisFlagFast}, 1, 1, 1) 627 registerCommand("ZScore", execZScore, readFirstKey, nil, 3, flagReadOnly). 628 attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1) 629 registerCommand("ZIncrBy", execZIncrBy, writeFirstKey, undoZIncr, 4, flagWrite). 630 attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM, redisFlagFast}, 1, 1, 1) 631 registerCommand("ZRank", execZRank, readFirstKey, nil, 3, flagReadOnly). 632 attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1) 633 registerCommand("ZCount", execZCount, readFirstKey, nil, 4, flagReadOnly). 634 attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1) 635 registerCommand("ZRevRank", execZRevRank, readFirstKey, nil, 3, flagReadOnly). 636 attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1) 637 registerCommand("ZCard", execZCard, readFirstKey, nil, 2, flagReadOnly). 638 attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1) 639 registerCommand("ZRange", execZRange, readFirstKey, nil, -4, flagReadOnly). 640 attachCommandExtra([]string{redisFlagReadonly}, 1, 1, 1) 641 registerCommand("ZRangeByScore", execZRangeByScore, readFirstKey, nil, -4, flagReadOnly). 642 attachCommandExtra([]string{redisFlagReadonly}, 1, 1, 1) 643 registerCommand("ZRevRange", execZRevRange, readFirstKey, nil, -4, flagReadOnly). 644 attachCommandExtra([]string{redisFlagReadonly}, 1, 1, 1) 645 registerCommand("ZRevRangeByScore", execZRevRangeByScore, readFirstKey, nil, -4, flagReadOnly). 646 attachCommandExtra([]string{redisFlagReadonly}, 1, 1, 1) 647 registerCommand("ZPopMin", execZPopMin, writeFirstKey, rollbackFirstKey, -2, flagWrite). 648 attachCommandExtra([]string{redisFlagWrite, redisFlagFast}, 1, 1, 1) 649 registerCommand("ZRem", execZRem, writeFirstKey, undoZRem, -3, flagWrite). 650 attachCommandExtra([]string{redisFlagWrite, redisFlagFast}, 1, 1, 1) 651 registerCommand("ZRemRangeByScore", execZRemRangeByScore, writeFirstKey, rollbackFirstKey, 4, flagWrite). 652 attachCommandExtra([]string{redisFlagWrite}, 1, 1, 1) 653 registerCommand("ZRemRangeByRank", execZRemRangeByRank, writeFirstKey, rollbackFirstKey, 4, flagWrite). 654 attachCommandExtra([]string{redisFlagWrite}, 1, 1, 1) 655 }