Skip to main content

yo_kv/
sort.rs

1//! `SORT` and `SORT_RO`, the one keyspace command that reads keys nobody named.
2//!
3//! Everything else in [`crate::keyspace`] asks a question about the key in front
4//! of it. `SORT mylist BY weight_* GET data_*` reads `mylist`, then reads a
5//! different key for every element in it to decide the order, then reads another
6//! one for every element to decide what to answer with. That is why it is the
7//! command Redis marks as not deterministic, why `SORT_RO` exists at all, and
8//! why it lives in a file of its own instead of as another arm of the keyspace
9//! match.
10//!
11//! # What it sorts
12//!
13//! A list, a set or a sorted set. Anything else is `WRONGTYPE` and a missing key
14//! is an empty answer, not an error.
15//!
16//! # The four ways it can order things
17//!
18//! Numerically on the elements, which is the default and which fails the whole
19//! command if any element is not a number. Numerically on a `BY` lookup, where
20//! an element whose lookup missed scores zero. Alphabetically on the elements.
21//! Alphabetically on a `BY` lookup, where a miss sorts before every hit.
22//!
23//! Ties never fall through to whatever order the elements arrived in. Two equal
24//! scores are broken by comparing the elements themselves, which is what makes
25//! the answer the same on two servers holding the same set, and a set has no
26//! order of its own to fall back on anyway.
27//!
28//! # Not sorting
29//!
30//! A `BY` pattern with no `*` in it cannot name a different key per element, so
31//! Redis reads it as an instruction not to sort rather than as a pattern that
32//! resolves to one key for everything. `BY nosort` is the idiom and there is
33//! nothing special about the word.
34//!
35//! There is one hole in that, and it is the reason the `store` flag is in
36//! [`Sort`] rather than being left to the caller. A set has no order, so
37//! `SORT myset BY nosort` may answer its members in any order at all, which is
38//! fine for a client that asked for exactly that and is not fine for a `STORE`,
39//! because then two servers that agree about the set disagree about the list
40//! they wrote. So a `BY nosort` over a set with a `STORE` sorts alphabetically
41//! after all. Redis does the same thing for the same reason, and also does it
42//! for a call from a script, which we do not, because a script here reaches the
43//! same method any other caller does.
44//!
45//! # Patterns
46//!
47//! A pattern is not a glob. The first `*` in it is replaced with the element and
48//! the result is a key name, so `weight_*` on the element `a` reads `weight_a`.
49//! A pattern with no `*` looks up nothing and misses every time. `#` on its own
50//! means the element itself, which is only useful in a `GET`.
51//!
52//! A pattern may reach into a hash instead of at a string, with `->` after the
53//! `*`: `h_*->field` reads the field `field` of the hash `h_<element>`. The
54//! split is on the first `->` that comes after the `*` and there has to be at
55//! least one byte after it, so a pattern ending in `->` is a key name with a
56//! `->` on the end and not a hash lookup with an empty field.
57//!
58//! A lookup that lands on a key of the wrong type is a miss and not an error.
59//! `BY h_*` over keys holding lists gives every element a weight of nothing,
60//! which sorts them all equal and lets the tie break do the work.
61//!
62//! # Divergence
63//!
64//! Redis compares strings with `strcoll` when there is no `STORE`, and with a
65//! byte compare when there is, because a stored result has to be the same on
66//! every replica and `strcoll` answers to `LC_COLLATE`. Redis calls
67//! `setlocale(LC_COLLATE, "")` at startup, so the order `SORT ... ALPHA` puts
68//! two strings in depends on the environment the server was started with, and
69//! the same server started twice can answer differently.
70//!
71//! This compares bytes always. That is the `STORE` behaviour applied everywhere,
72//! it is what a client gets from Redis under the C locale, and it is the only
73//! choice that makes the answer a property of the data. It also means a member
74//! with a zero byte in it sorts on all of its bytes, where `strcoll` stops at
75//! the zero. Registered in `divergences.toml`.
76
77use crate::keyspace::{Keyspace, wrong_type};
78use crate::value::Kind;
79use crate::zsets::Window;
80use std::cmp::Ordering;
81use yo_common::{Code, Error, Result, num};
82
83/// What Redis says when a numeric sort meets an element that is not a number.
84const NOT_A_DOUBLE: &str = "One or more scores can't be converted into double";
85
86/// What `SORT` was asked to do, everything except the key and the destination.
87///
88/// Borrowed from the caller's arguments rather than owned, because on the wire
89/// path every field of this is a slice of the command that is already in memory
90/// and copying them would be copying the command.
91#[derive(Debug, Clone, Copy, Default)]
92pub struct Sort<'a> {
93    /// The `BY` pattern, if there was one.
94    pub by: Option<&'a [u8]>,
95    /// The `GET` patterns, in the order they were given, `#` included.
96    pub get: &'a [&'a [u8]],
97    /// `LIMIT offset count`, if there was one.
98    ///
99    /// Both halves are signed because both halves are on the wire as integers
100    /// and Redis takes them without complaint. A negative offset is clamped to
101    /// the front and a negative count means everything from the offset on.
102    pub limit: Option<(i64, i64)>,
103    /// `DESC`, which reverses whatever order the rest of this produced.
104    pub desc: bool,
105    /// `ALPHA`, which compares bytes where the default compares numbers.
106    pub alpha: bool,
107    /// Whether the answer is going into a key rather than back to the caller.
108    ///
109    /// Set by [`Keyspace::sort_store`] and not by the caller. It is in here
110    /// rather than a separate argument because it changes the ordering and not
111    /// just what happens to the result. See the module doc.
112    pub store: bool,
113}
114
115/// One element on its way through the sort, with whatever it is ordered by.
116///
117/// The weight is one of the two, never both, and which one is decided once for
118/// the whole command rather than per element.
119struct Weighted {
120    /// The element itself, which is also the tie break and may be the answer.
121    elem: Vec<u8>,
122    /// The number to sort on, under a numeric sort.
123    score: f64,
124    /// The bytes to sort on, under an alphabetic sort with a `BY`. `None` is a
125    /// lookup that missed and sorts before every hit.
126    text: Option<Vec<u8>>,
127}
128
129impl Keyspace {
130    /// `SORT key [BY pattern] [LIMIT offset count] [GET pattern ...] [ASC|DESC]
131    /// [ALPHA]`, and the whole of `SORT_RO`.
132    ///
133    /// One row per element that survived the `LIMIT`, or one row per `GET`
134    /// pattern per element if there were any. A row is `None` where a `GET`
135    /// pattern missed, which is a nil on the wire, and `GET #` never misses.
136    ///
137    /// This allocates the elements, which nothing else on the read path does. It
138    /// has to: the ordering is decided by reading other keys, and reading
139    /// another key needs the database that the elements are borrowed from. Redis
140    /// has the same problem and solves it by holding refcounted pointers, which
141    /// is the same copy with the copy moved to whoever wrote the value.
142    pub fn sort(&mut self, key: &[u8], opts: &Sort<'_>) -> Result<Vec<Option<Vec<u8>>>> {
143        let mut opts = *opts;
144        opts.store = false;
145        self.sorted(key, &opts)
146    }
147
148    /// `SORT key ... STORE destination`, which answers the length of the list it
149    /// wrote.
150    ///
151    /// An empty result deletes the destination rather than leaving an empty list
152    /// behind, because a list is never empty and a key that holds one that is
153    /// would be a key `TYPE` answers `list` for and `LLEN` answers zero for.
154    ///
155    /// A `GET` pattern that missed stores an empty string, where the same miss
156    /// sent to a client is a nil. There is no nil in a list, so this is the only
157    /// thing it could be, and it is what Redis stores.
158    pub fn sort_store(&mut self, key: &[u8], dest: &[u8], opts: &Sort<'_>) -> Result<usize> {
159        let mut opts = *opts;
160        opts.store = true;
161        let rows = self.sorted(key, &opts)?;
162        if rows.is_empty() {
163            self.del(dest);
164            return Ok(0);
165        }
166        // Written into a fresh key rather than appended to whatever was there,
167        // and the delete comes first so that `SORT k STORE k` reads k, then
168        // throws it away, then writes the answer. Redis is the same, and it is
169        // the reason the elements had to be copied out before any of this.
170        self.del(dest);
171        let owned: Vec<Vec<u8>> = rows.into_iter().map(Option::unwrap_or_default).collect();
172        self.push(
173            dest,
174            crate::lists::End::Right,
175            owned.iter().map(Vec::as_slice),
176        )
177    }
178
179    /// The body both of them share.
180    fn sorted(&mut self, key: &[u8], opts: &Sort<'_>) -> Result<Vec<Option<Vec<u8>>>> {
181        let kind = self.kind_of(key);
182        let elems = self.elements(key, kind)?;
183
184        // A `BY` with no `*` cannot name a key per element, so it is an order to
185        // leave things alone. The exception is the one the module doc explains.
186        let mut alpha = opts.alpha;
187        let mut by = opts.by;
188        let mut dontsort = by.is_some_and(|p| !p.contains(&b'*'));
189        if dontsort && kind == Some(Kind::Set) && opts.store {
190            dontsort = false;
191            alpha = true;
192            by = None;
193        }
194
195        let ordered = if dontsort {
196            // The natural order of whatever it is, backwards if `DESC` was
197            // given. A list is head to tail, a sorted set is by score, and a set
198            // has no order to reverse but reversing it costs nothing and keeps
199            // this one branch instead of two.
200            let mut e = elems;
201            if opts.desc {
202                e.reverse();
203            }
204            e
205        } else {
206            self.order(elems, by, alpha, opts.desc)?
207        };
208
209        let window = limit(ordered.len(), opts.limit);
210        self.emit(&ordered[window], opts.get)
211    }
212
213    /// Copy the elements out of whatever holds them.
214    ///
215    /// Owned, for the reason [`Keyspace::sort`] gives. A missing key is an empty
216    /// list and not an error, so `SORT nosuchkey` answers nothing.
217    fn elements(&mut self, key: &[u8], kind: Option<Kind>) -> Result<Vec<Vec<u8>>> {
218        let mut out = Vec::new();
219        match kind {
220            None => {}
221            Some(Kind::List) => {
222                for e in self.lrange(key, 0, -1)? {
223                    let mut v = Vec::new();
224                    e.write_to(&mut v);
225                    out.push(v);
226                }
227            }
228            Some(Kind::Set) => {
229                if let Some(members) = self.smembers(key)? {
230                    for m in members {
231                        let mut v = Vec::new();
232                        m.write_to(&mut v);
233                        out.push(v);
234                    }
235                }
236            }
237            Some(Kind::Zset) => {
238                let n = self.zcard(key)?;
239                let w = Window {
240                    from: 0,
241                    count: n,
242                    rev: false,
243                };
244                out.reserve(n);
245                self.zwalk(key, w, |m, _| {
246                    let mut v = Vec::new();
247                    m.write_to(&mut v);
248                    out.push(v);
249                })?;
250            }
251            Some(_) => return Err(wrong_type()),
252        }
253        Ok(out)
254    }
255
256    /// Weigh every element and put them in order.
257    fn order(
258        &mut self,
259        elems: Vec<Vec<u8>>,
260        by: Option<&[u8]>,
261        alpha: bool,
262        desc: bool,
263    ) -> Result<Vec<Vec<u8>>> {
264        let mut weighed = Vec::with_capacity(elems.len());
265        for elem in elems {
266            let looked = match by {
267                Some(pattern) => self.by_pattern(pattern, &elem),
268                None => None,
269            };
270            let (score, text) = if alpha {
271                // Without a `BY` the element is its own sort key, and the tie
272                // break already compares elements, so there is nothing to carry.
273                (0.0, if by.is_some() { looked } else { None })
274            } else {
275                let raw = match by {
276                    // A lookup that missed weighs nothing. Redis leaves the
277                    // score at zero rather than failing, which means a numeric
278                    // `BY` over keys that do not exist is a pure tie break.
279                    Some(_) => match looked {
280                        Some(v) => v,
281                        None => {
282                            weighed.push(Weighted {
283                                elem,
284                                score: 0.0,
285                                text: None,
286                            });
287                            continue;
288                        }
289                    },
290                    None => elem.clone(),
291                };
292                let n =
293                    num::parse_f64(&raw).ok_or_else(|| Error::new(Code::Invalid, NOT_A_DOUBLE))?;
294                if n.is_nan() {
295                    return Err(Error::new(Code::Invalid, NOT_A_DOUBLE));
296                }
297                (n, None)
298            };
299            weighed.push(Weighted { elem, score, text });
300        }
301
302        // Stable is not needed, since the tie break is total, but it is what
303        // `sort_by` gives and asking for the unstable one to save nothing would
304        // be trading a guarantee for no gain.
305        weighed.sort_by(|a, b| {
306            let cmp = if alpha {
307                match (&a.text, &b.text) {
308                    // Both missing, or no `BY` at all, so the elements decide.
309                    (None, None) => a.elem.cmp(&b.elem),
310                    // A miss sorts before a hit.
311                    (None, Some(_)) => Ordering::Less,
312                    (Some(_), None) => Ordering::Greater,
313                    (Some(x), Some(y)) => x.cmp(y).then_with(|| a.elem.cmp(&b.elem)),
314                }
315            } else {
316                // No NaN can reach here, so the partial compare is total.
317                a.score
318                    .partial_cmp(&b.score)
319                    .unwrap_or(Ordering::Equal)
320                    .then_with(|| a.elem.cmp(&b.elem))
321            };
322            if desc { cmp.reverse() } else { cmp }
323        });
324        Ok(weighed.into_iter().map(|w| w.elem).collect())
325    }
326
327    /// Build the answer, which is the elements themselves or a `GET` per element.
328    fn emit(&mut self, elems: &[Vec<u8>], get: &[&[u8]]) -> Result<Vec<Option<Vec<u8>>>> {
329        if get.is_empty() {
330            return Ok(elems.iter().map(|e| Some(e.clone())).collect());
331        }
332        let mut out = Vec::with_capacity(elems.len() * get.len());
333        for elem in elems {
334            for pattern in get {
335                if *pattern == b"#" {
336                    out.push(Some(elem.clone()));
337                } else {
338                    out.push(self.by_pattern(pattern, elem));
339                }
340            }
341        }
342        Ok(out)
343    }
344
345    /// Read the key a pattern names for one element.
346    ///
347    /// `None` for a pattern with no `*`, for a key that is not there, and for a
348    /// key that is there and holds the wrong type. The last one is a miss rather
349    /// than an error on purpose: a pattern is a guess about a naming convention
350    /// and one key that does not fit the convention should not fail a command
351    /// over ten thousand elements.
352    fn by_pattern(&mut self, pattern: &[u8], elem: &[u8]) -> Option<Vec<u8>> {
353        let star = pattern.iter().position(|&c| c == b'*')?;
354        // The field split is looked for after the `*`, so a `->` in the prefix
355        // is part of the key name. And there has to be something after it, so a
356        // pattern ending in `->` names a key whose name ends in `->`.
357        let arrow = pattern[star + 1..]
358            .windows(2)
359            .position(|w| w == b"->")
360            .map(|i| star + 1 + i)
361            .filter(|&i| i + 2 < pattern.len());
362
363        let (key_part, field) = match arrow {
364            Some(i) => (&pattern[..i], Some(&pattern[i + 2..])),
365            None => (pattern, None),
366        };
367
368        let mut key = Vec::with_capacity(key_part.len() + elem.len());
369        key.extend_from_slice(&key_part[..star]);
370        key.extend_from_slice(elem);
371        key.extend_from_slice(&key_part[star + 1..]);
372
373        match field {
374            Some(f) => self
375                .hget(&key, f, |t| {
376                    t.map(|t| {
377                        let mut v = Vec::new();
378                        t.write_to(&mut v);
379                        v
380                    })
381                })
382                .unwrap_or(None),
383            None => self.get(&key).ok().flatten().map(|s| s.to_vec()),
384        }
385    }
386}
387
388/// Which slice of the sorted elements the `LIMIT` asked for.
389///
390/// Redis clamps rather than complains at every edge: a negative offset is the
391/// front, a negative count is everything left, an offset past the end is an
392/// empty answer and a count that runs past the end stops at it.
393fn limit(len: usize, limit: Option<(i64, i64)>) -> std::ops::Range<usize> {
394    let Some((offset, count)) = limit else {
395        return 0..len;
396    };
397    let start = usize::try_from(offset).unwrap_or(0).min(len);
398    let end = if count < 0 {
399        len
400    } else {
401        start
402            .saturating_add(usize::try_from(count).unwrap_or(0))
403            .min(len)
404    };
405    start..end
406}
407
408#[cfg(test)]
409mod tests {
410    use super::*;
411    use crate::lists::End;
412    use crate::strings::SetOptions;
413    use crate::zsets::ZAdd;
414
415    /// The answer as flat bytes, with a missed `GET` written as the word `nil`,
416    /// which no test here stores as a value.
417    fn flat(rows: Vec<Option<Vec<u8>>>) -> Vec<String> {
418        rows.into_iter()
419            .map(|r| match r {
420                Some(v) => String::from_utf8_lossy(&v).into_owned(),
421                None => "nil".to_string(),
422            })
423            .collect()
424    }
425
426    /// One list element as a string, for reading back what a `STORE` wrote.
427    fn text(e: crate::listpack::Entry<'_>) -> String {
428        let mut v = Vec::new();
429        e.write_to(&mut v);
430        String::from_utf8_lossy(&v).into_owned()
431    }
432
433    fn list(db: &mut Keyspace, key: &[u8], items: &[&str]) {
434        db.push(key, End::Right, items.iter().map(|s| s.as_bytes()))
435            .expect("a fresh list takes elements");
436    }
437
438    #[test]
439    fn numbers_sort_as_numbers_and_not_as_text() {
440        let mut db = Keyspace::new();
441        list(&mut db, b"l", &["10", "9", "100", "1"]);
442        let opts = Sort::default();
443        assert_eq!(flat(db.sort(b"l", &opts).unwrap()), ["1", "9", "10", "100"]);
444        let alpha = Sort {
445            alpha: true,
446            ..Sort::default()
447        };
448        assert_eq!(
449            flat(db.sort(b"l", &alpha).unwrap()),
450            ["1", "10", "100", "9"]
451        );
452    }
453
454    #[test]
455    fn an_element_that_is_not_a_number_fails_the_whole_command() {
456        let mut db = Keyspace::new();
457        list(&mut db, b"l", &["1", "two", "3"]);
458        let err = db.sort(b"l", &Sort::default()).unwrap_err();
459        assert_eq!(err.message(), NOT_A_DOUBLE);
460        // And the same elements under ALPHA are fine, which is the whole point
461        // of the option.
462        let alpha = Sort {
463            alpha: true,
464            ..Sort::default()
465        };
466        assert_eq!(flat(db.sort(b"l", &alpha).unwrap()), ["1", "3", "two"]);
467    }
468
469    #[test]
470    fn desc_reverses_and_limit_takes_a_window_of_what_is_left() {
471        let mut db = Keyspace::new();
472        list(&mut db, b"l", &["3", "1", "5", "2", "4"]);
473        let opts = Sort {
474            desc: true,
475            limit: Some((1, 2)),
476            ..Sort::default()
477        };
478        assert_eq!(flat(db.sort(b"l", &opts).unwrap()), ["4", "3"]);
479        // A negative count is everything from the offset on, and an offset past
480        // the end is nothing at all.
481        let rest = Sort {
482            limit: Some((3, -1)),
483            ..Sort::default()
484        };
485        assert_eq!(flat(db.sort(b"l", &rest).unwrap()), ["4", "5"]);
486        let past = Sort {
487            limit: Some((99, 5)),
488            ..Sort::default()
489        };
490        assert!(db.sort(b"l", &past).unwrap().is_empty());
491        let before = Sort {
492            limit: Some((-4, 2)),
493            ..Sort::default()
494        };
495        assert_eq!(flat(db.sort(b"l", &before).unwrap()), ["1", "2"]);
496    }
497
498    #[test]
499    fn by_reads_a_key_for_every_element() {
500        let mut db = Keyspace::new();
501        list(&mut db, b"l", &["a", "b", "c"]);
502        db.set(b"w_a", b"3", SetOptions::PLAIN).unwrap();
503        db.set(b"w_b", b"1", SetOptions::PLAIN).unwrap();
504        db.set(b"w_c", b"2", SetOptions::PLAIN).unwrap();
505        let opts = Sort {
506            by: Some(b"w_*"),
507            ..Sort::default()
508        };
509        assert_eq!(flat(db.sort(b"l", &opts).unwrap()), ["b", "c", "a"]);
510    }
511
512    #[test]
513    fn a_by_lookup_that_missed_weighs_nothing_and_the_element_breaks_the_tie() {
514        let mut db = Keyspace::new();
515        list(&mut db, b"l", &["c", "a", "b"]);
516        db.set(b"w_b", b"5", SetOptions::PLAIN).unwrap();
517        let opts = Sort {
518            by: Some(b"w_*"),
519            ..Sort::default()
520        };
521        // `a` and `c` both weigh zero, so they come first in element order, and
522        // `b` weighs five and comes last.
523        assert_eq!(flat(db.sort(b"l", &opts).unwrap()), ["a", "c", "b"]);
524    }
525
526    #[test]
527    fn under_alpha_a_missed_by_sorts_before_every_hit() {
528        let mut db = Keyspace::new();
529        list(&mut db, b"l", &["c", "a", "b"]);
530        db.set(b"w_b", b"zzz", SetOptions::PLAIN).unwrap();
531        db.set(b"w_c", b"aaa", SetOptions::PLAIN).unwrap();
532        let opts = Sort {
533            by: Some(b"w_*"),
534            alpha: true,
535            ..Sort::default()
536        };
537        assert_eq!(flat(db.sort(b"l", &opts).unwrap()), ["a", "c", "b"]);
538    }
539
540    #[test]
541    fn a_pattern_can_reach_into_a_hash() {
542        let mut db = Keyspace::new();
543        list(&mut db, b"l", &["a", "b"]);
544        db.hset(b"h_a", [(&b"w"[..], &b"2"[..])].into_iter())
545            .unwrap();
546        db.hset(b"h_b", [(&b"w"[..], &b"1"[..])].into_iter())
547            .unwrap();
548        let opts = Sort {
549            by: Some(b"h_*->w"),
550            ..Sort::default()
551        };
552        assert_eq!(flat(db.sort(b"l", &opts).unwrap()), ["b", "a"]);
553        // And a pattern that ends in an arrow is a key name, not a hash lookup
554        // with no field, so it reads a string key called `h_a->`.
555        db.set(b"h_a->", b"9", SetOptions::PLAIN).unwrap();
556        let trailing = Sort {
557            by: Some(b"h_*->"),
558            ..Sort::default()
559        };
560        assert_eq!(flat(db.sort(b"l", &trailing).unwrap()), ["b", "a"]);
561    }
562
563    #[test]
564    fn get_answers_other_keys_and_a_hash_of_them() {
565        let mut db = Keyspace::new();
566        list(&mut db, b"l", &["2", "1"]);
567        db.set(b"d_1", b"one", SetOptions::PLAIN).unwrap();
568        db.set(b"d_2", b"two", SetOptions::PLAIN).unwrap();
569        let get: [&[u8]; 2] = [b"#", b"d_*"];
570        let opts = Sort {
571            get: &get,
572            ..Sort::default()
573        };
574        assert_eq!(
575            flat(db.sort(b"l", &opts).unwrap()),
576            ["1", "one", "2", "two"]
577        );
578        // A miss is a nil and not a skipped row, because the reply is positional.
579        db.del(b"d_2");
580        assert_eq!(
581            flat(db.sort(b"l", &opts).unwrap()),
582            ["1", "one", "2", "nil"]
583        );
584    }
585
586    #[test]
587    fn a_lookup_at_the_wrong_type_is_a_miss_and_not_an_error() {
588        let mut db = Keyspace::new();
589        list(&mut db, b"l", &["a"]);
590        list(&mut db, b"d_a", &["x"]);
591        let get: [&[u8]; 1] = [b"d_*"];
592        let opts = Sort {
593            get: &get,
594            alpha: true,
595            ..Sort::default()
596        };
597        assert_eq!(flat(db.sort(b"l", &opts).unwrap()), ["nil"]);
598    }
599
600    #[test]
601    fn by_without_a_star_leaves_the_order_alone() {
602        let mut db = Keyspace::new();
603        list(&mut db, b"l", &["3", "1", "2"]);
604        let opts = Sort {
605            by: Some(b"nosort"),
606            ..Sort::default()
607        };
608        assert_eq!(flat(db.sort(b"l", &opts).unwrap()), ["3", "1", "2"]);
609        // And DESC still reverses it, because there is an order to reverse.
610        let back = Sort {
611            by: Some(b"nosort"),
612            desc: true,
613            ..Sort::default()
614        };
615        assert_eq!(flat(db.sort(b"l", &back).unwrap()), ["2", "1", "3"]);
616    }
617
618    #[test]
619    fn a_set_stored_without_a_sort_is_sorted_anyway() {
620        let mut db = Keyspace::new();
621        for m in ["c", "a", "b"] {
622            db.sadd(b"s", [m.as_bytes()].into_iter()).unwrap();
623        }
624        let opts = Sort {
625            by: Some(b"nosort"),
626            ..Sort::default()
627        };
628        assert_eq!(db.sort_store(b"s", b"out", &opts).unwrap(), 3);
629        let got: Vec<String> = db.lrange(b"out", 0, -1).unwrap().map(text).collect();
630        assert_eq!(got, ["a", "b", "c"]);
631    }
632
633    #[test]
634    fn a_sorted_set_comes_out_in_score_order_when_nothing_says_otherwise() {
635        let mut db = Keyspace::new();
636        db.zadd(
637            b"z",
638            [(3.0, &b"c"[..]), (1.0, &b"a"[..]), (2.0, &b"b"[..])].into_iter(),
639            ZAdd::default(),
640        )
641        .unwrap();
642        let opts = Sort {
643            by: Some(b"nosort"),
644            ..Sort::default()
645        };
646        assert_eq!(flat(db.sort(b"z", &opts).unwrap()), ["a", "b", "c"]);
647    }
648
649    #[test]
650    fn storing_an_empty_result_removes_the_destination() {
651        let mut db = Keyspace::new();
652        list(&mut db, b"out", &["stale"]);
653        assert_eq!(
654            db.sort_store(b"missing", b"out", &Sort::default()).unwrap(),
655            0
656        );
657        assert!(!db.exists(b"out"));
658    }
659
660    #[test]
661    fn a_stored_get_that_missed_is_an_empty_string() {
662        let mut db = Keyspace::new();
663        list(&mut db, b"l", &["1"]);
664        let get: [&[u8]; 1] = [b"d_*"];
665        let opts = Sort {
666            get: &get,
667            ..Sort::default()
668        };
669        assert_eq!(db.sort_store(b"l", b"out", &opts).unwrap(), 1);
670        assert_eq!(db.llen(b"out").unwrap(), 1);
671    }
672
673    #[test]
674    fn a_missing_key_is_empty_and_a_wrong_type_is_an_error() {
675        let mut db = Keyspace::new();
676        assert!(db.sort(b"nosuchkey", &Sort::default()).unwrap().is_empty());
677        db.set(b"str", b"x", SetOptions::PLAIN).unwrap();
678        assert_eq!(
679            db.sort(b"str", &Sort::default()).unwrap_err().code(),
680            Code::WrongType
681        );
682    }
683
684    #[test]
685    fn sorting_into_the_key_being_sorted_works() {
686        let mut db = Keyspace::new();
687        list(&mut db, b"l", &["3", "1", "2"]);
688        assert_eq!(db.sort_store(b"l", b"l", &Sort::default()).unwrap(), 3);
689        let got: Vec<String> = db.lrange(b"l", 0, -1).unwrap().map(text).collect();
690        assert_eq!(got, ["1", "2", "3"]);
691    }
692}