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