1start_server { 2 tags {"sort"} 3 overrides { 4 "list-max-ziplist-value" 16 5 "list-max-ziplist-entries" 32 6 "set-max-intset-entries" 32 7 } 8} { 9 proc create_random_dataset {num cmd} { 10 set tosort {} 11 set result {} 12 array set seenrand {} 13 r del tosort 14 for {set i 0} {$i < $num} {incr i} { 15 # Make sure all the weights are different because 16 # Redis does not use a stable sort but Tcl does. 17 while 1 { 18 randpath { 19 set rint [expr int(rand()*1000000)] 20 } { 21 set rint [expr rand()] 22 } 23 if {![info exists seenrand($rint)]} break 24 } 25 set seenrand($rint) x 26 r $cmd tosort $i 27 r set weight_$i $rint 28 r hset wobj_$i weight $rint 29 lappend tosort [list $i $rint] 30 } 31 set sorted [lsort -index 1 -real $tosort] 32 for {set i 0} {$i < $num} {incr i} { 33 lappend result [lindex $sorted $i 0] 34 } 35 set _ $result 36 } 37 38 foreach {num cmd enc title} { 39 16 lpush ziplist "Ziplist" 40 1000 lpush linkedlist "Linked list" 41 10000 lpush linkedlist "Big Linked list" 42 16 sadd intset "Intset" 43 1000 sadd hashtable "Hash table" 44 10000 sadd hashtable "Big Hash table" 45 } { 46 set result [create_random_dataset $num $cmd] 47 assert_encoding $enc tosort 48 49 test "$title: SORT BY key" { 50 assert_equal $result [r sort tosort BY weight_*] 51 } 52 53 test "$title: SORT BY key with limit" { 54 assert_equal [lrange $result 5 9] [r sort tosort BY weight_* LIMIT 5 5] 55 } 56 57 test "$title: SORT BY hash field" { 58 assert_equal $result [r sort tosort BY wobj_*->weight] 59 } 60 } 61 62 set result [create_random_dataset 16 lpush] 63 test "SORT GET #" { 64 assert_equal [lsort -integer $result] [r sort tosort GET #] 65 } 66 67 test "SORT GET <const>" { 68 r del foo 69 set res [r sort tosort GET foo] 70 assert_equal 16 [llength $res] 71 foreach item $res { assert_equal {} $item } 72 } 73 74 test "SORT GET (key and hash) with sanity check" { 75 set l1 [r sort tosort GET # GET weight_*] 76 set l2 [r sort tosort GET # GET wobj_*->weight] 77 foreach {id1 w1} $l1 {id2 w2} $l2 { 78 assert_equal $id1 $id2 79 assert_equal $w1 [r get weight_$id1] 80 assert_equal $w2 [r get weight_$id1] 81 } 82 } 83 84 test "SORT BY key STORE" { 85 r sort tosort BY weight_* store sort-res 86 assert_equal $result [r lrange sort-res 0 -1] 87 assert_equal 16 [r llen sort-res] 88 assert_encoding ziplist sort-res 89 } 90 91 test "SORT BY hash field STORE" { 92 r sort tosort BY wobj_*->weight store sort-res 93 assert_equal $result [r lrange sort-res 0 -1] 94 assert_equal 16 [r llen sort-res] 95 assert_encoding ziplist sort-res 96 } 97 98 test "SORT extracts STORE correctly" { 99 r command getkeys sort abc store def 100 } {abc def} 101 102 test "SORT extracts multiple STORE correctly" { 103 r command getkeys sort abc store invalid store stillbad store def 104 } {abc def} 105 106 test "SORT DESC" { 107 assert_equal [lsort -decreasing -integer $result] [r sort tosort DESC] 108 } 109 110 test "SORT ALPHA against integer encoded strings" { 111 r del mylist 112 r lpush mylist 2 113 r lpush mylist 1 114 r lpush mylist 3 115 r lpush mylist 10 116 r sort mylist alpha 117 } {1 10 2 3} 118 119 test "SORT sorted set" { 120 r del zset 121 r zadd zset 1 a 122 r zadd zset 5 b 123 r zadd zset 2 c 124 r zadd zset 10 d 125 r zadd zset 3 e 126 r sort zset alpha desc 127 } {e d c b a} 128 129 test "SORT sorted set BY nosort should retain ordering" { 130 r del zset 131 r zadd zset 1 a 132 r zadd zset 5 b 133 r zadd zset 2 c 134 r zadd zset 10 d 135 r zadd zset 3 e 136 r multi 137 r sort zset by nosort asc 138 r sort zset by nosort desc 139 r exec 140 } {{a c e b d} {d b e c a}} 141 142 test "SORT sorted set BY nosort + LIMIT" { 143 r del zset 144 r zadd zset 1 a 145 r zadd zset 5 b 146 r zadd zset 2 c 147 r zadd zset 10 d 148 r zadd zset 3 e 149 assert_equal [r sort zset by nosort asc limit 0 1] {a} 150 assert_equal [r sort zset by nosort desc limit 0 1] {d} 151 assert_equal [r sort zset by nosort asc limit 0 2] {a c} 152 assert_equal [r sort zset by nosort desc limit 0 2] {d b} 153 assert_equal [r sort zset by nosort limit 5 10] {} 154 assert_equal [r sort zset by nosort limit -10 100] {a c e b d} 155 } 156 157 test "SORT sorted set BY nosort works as expected from scripts" { 158 r del zset 159 r zadd zset 1 a 160 r zadd zset 5 b 161 r zadd zset 2 c 162 r zadd zset 10 d 163 r zadd zset 3 e 164 r eval { 165 return {redis.call('sort',KEYS[1],'by','nosort','asc'), 166 redis.call('sort',KEYS[1],'by','nosort','desc')} 167 } 1 zset 168 } {{a c e b d} {d b e c a}} 169 170 test "SORT sorted set: +inf and -inf handling" { 171 r del zset 172 r zadd zset -100 a 173 r zadd zset 200 b 174 r zadd zset -300 c 175 r zadd zset 1000000 d 176 r zadd zset +inf max 177 r zadd zset -inf min 178 r zrange zset 0 -1 179 } {min c a b d max} 180 181 test "SORT regression for issue #19, sorting floats" { 182 r flushdb 183 set floats {1.1 5.10 3.10 7.44 2.1 5.75 6.12 0.25 1.15} 184 foreach x $floats { 185 r lpush mylist $x 186 } 187 assert_equal [lsort -real $floats] [r sort mylist] 188 } 189 190 test "SORT with STORE returns zero if result is empty (github issue 224)" { 191 r flushdb 192 r sort foo store bar 193 } {0} 194 195 test "SORT with STORE does not create empty lists (github issue 224)" { 196 r flushdb 197 r lpush foo bar 198 r sort foo alpha limit 10 10 store zap 199 r exists zap 200 } {0} 201 202 test "SORT with STORE removes key if result is empty (github issue 227)" { 203 r flushdb 204 r lpush foo bar 205 r sort emptylist store foo 206 r exists foo 207 } {0} 208 209 test "SORT with BY <constant> and STORE should still order output" { 210 r del myset mylist 211 r sadd myset a b c d e f g h i l m n o p q r s t u v z aa aaa azz 212 r sort myset alpha by _ store mylist 213 r lrange mylist 0 -1 214 } {a aa aaa azz b c d e f g h i l m n o p q r s t u v z} 215 216 test "SORT will complain with numerical sorting and bad doubles (1)" { 217 r del myset 218 r sadd myset 1 2 3 4 not-a-double 219 set e {} 220 catch {r sort myset} e 221 set e 222 } {*ERR*double*} 223 224 test "SORT will complain with numerical sorting and bad doubles (2)" { 225 r del myset 226 r sadd myset 1 2 3 4 227 r mset score:1 10 score:2 20 score:3 30 score:4 not-a-double 228 set e {} 229 catch {r sort myset by score:*} e 230 set e 231 } {*ERR*double*} 232 233 test "SORT BY sub-sorts lexicographically if score is the same" { 234 r del myset 235 r sadd myset a b c d e f g h i l m n o p q r s t u v z aa aaa azz 236 foreach ele {a aa aaa azz b c d e f g h i l m n o p q r s t u v z} { 237 set score:$ele 100 238 } 239 r sort myset by score:* 240 } {a aa aaa azz b c d e f g h i l m n o p q r s t u v z} 241 242 test "SORT GET with pattern ending with just -> does not get hash field" { 243 r del mylist 244 r lpush mylist a 245 r set x:a-> 100 246 r sort mylist by num get x:*-> 247 } {100} 248 249 tags {"slow"} { 250 set num 100 251 set res [create_random_dataset $num lpush] 252 253 test "SORT speed, $num element list BY key, 100 times" { 254 set start [clock clicks -milliseconds] 255 for {set i 0} {$i < 100} {incr i} { 256 set sorted [r sort tosort BY weight_* LIMIT 0 10] 257 } 258 set elapsed [expr [clock clicks -milliseconds]-$start] 259 if {$::verbose} { 260 puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds " 261 flush stdout 262 } 263 } 264 265 test "SORT speed, $num element list BY hash field, 100 times" { 266 set start [clock clicks -milliseconds] 267 for {set i 0} {$i < 100} {incr i} { 268 set sorted [r sort tosort BY wobj_*->weight LIMIT 0 10] 269 } 270 set elapsed [expr [clock clicks -milliseconds]-$start] 271 if {$::verbose} { 272 puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds " 273 flush stdout 274 } 275 } 276 277 test "SORT speed, $num element list directly, 100 times" { 278 set start [clock clicks -milliseconds] 279 for {set i 0} {$i < 100} {incr i} { 280 set sorted [r sort tosort LIMIT 0 10] 281 } 282 set elapsed [expr [clock clicks -milliseconds]-$start] 283 if {$::verbose} { 284 puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds " 285 flush stdout 286 } 287 } 288 289 test "SORT speed, $num element list BY <const>, 100 times" { 290 set start [clock clicks -milliseconds] 291 for {set i 0} {$i < 100} {incr i} { 292 set sorted [r sort tosort BY nokey LIMIT 0 10] 293 } 294 set elapsed [expr [clock clicks -milliseconds]-$start] 295 if {$::verbose} { 296 puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds " 297 flush stdout 298 } 299 } 300 } 301} 302