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