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