xref: /redis-3.2.3/tests/unit/sort.tcl (revision 3064e9bd)
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 isse 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