sort.tcl 9.3 KB

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