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