sort.tcl 9.5 KB

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