sort.tcl 10 KB

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