Skip to main content

yo_doc/
index.rs

1//! Indexes over a path into a document (`09` sections 4 and 5).
2//!
3//! A collection can find a document by its id already, because the primary
4//! table is keyed by it. An index is what makes it findable by what is inside
5//! it: one element table per indexed path, keyed by the value at that path,
6//! holding the ids of the documents that have it.
7//!
8//! An index answers equality, and an ordered one answers ranges as well. The
9//! array and text kinds file a document under more than one key at a time,
10//! every element of an array or every word of a string, and are asked the same
11//! question an equality index is.
12//!
13//! ```
14//! use std::ops::Bound;
15//! use yo_doc::{Builder, Docs, Key};
16//!
17//! let mut docs = Docs::new();
18//! docs.create_index("$.status")?;
19//! docs.create_ordered_index("$.price")?;
20//! docs.create_text_index("$.name")?;
21//! for (id, status, price, name) in [
22//!     ("a", "open", 30, "A red bicycle"),
23//!     ("b", "shut", 10, "a blue kite"),
24//!     ("c", "open", 20, "A red kite"),
25//! ] {
26//!     let mut b = Builder::new();
27//!     b.begin_object()?;
28//!     b.key(b"status")?;
29//!     b.text(status)?;
30//!     b.key(b"price")?;
31//!     b.int(price)?;
32//!     b.key(b"name")?;
33//!     b.text(name)?;
34//!     b.end_object()?;
35//!     let bytes = b.finish()?.to_vec();
36//!     docs.put_bytes(id.as_bytes(), &bytes)?;
37//! }
38//!
39//! let mut found = Vec::new();
40//! docs.find("$.status", &Key::text("open"), |id, _| found.push(id.to_vec()))?;
41//! found.sort();
42//! assert_eq!(found, [b"a".to_vec(), b"c".to_vec()]);
43//!
44//! // Cheapest first, up to and including twenty.
45//! let mut upto = Vec::new();
46//! docs.range("$.price", Bound::Unbounded, Bound::Included(&Key::int(20)), |id, _| {
47//!     upto.push(id.to_vec())
48//! })?;
49//! assert_eq!(upto, [b"b".to_vec(), b"c".to_vec()]);
50//!
51//! // One word out of the name, with the case folded on both sides.
52//! let red = Key::word("RED").expect("one word");
53//! let mut kites = Vec::new();
54//! docs.find("$.name", &red, |id, _| kites.push(id.to_vec()))?;
55//! kites.sort();
56//! assert_eq!(kites, [b"a".to_vec(), b"c".to_vec()]);
57//! # Ok::<(), yo_common::Error>(())
58//! ```
59//!
60//! # It is the same code again
61//!
62//! The table from key to posting list is [`Elements`], which is a hash's field
63//! table. The posting list is [`Set`], which is a Redis set, so a key that one
64//! document has costs a listpack entry rather than a hash table, a key that a
65//! million documents have is a partitioned element table, and a collection
66//! whose ids are numbers gets an intset and eight bytes a posting. None of that
67//! was written for this.
68//!
69//! It also means intersecting two indexes is `SINTER`, on the same sets, with
70//! the same code, at the same speed. That is the whole of `09` section 5's
71//! "probe each equality index, intersect the smallest result first", and there
72//! is nothing to build for it.
73//!
74//! The order an ordered index walks is the counted B+ tree from `08` section 5,
75//! which is what a sorted set ranks with. That tree holds row numbers and asks
76//! the caller to compare, so it took no changes at all to put index keys under
77//! it instead of zset members. It costs about three bytes per distinct value,
78//! and a range is one descent and then a link hop per leaf, so the cost of a
79//! range is the size of the answer rather than the size of the collection.
80//!
81//! The key table stays unordered either way, because it is a hash's field table
82//! and a hash is not ordered. The order is a separate structure over its row
83//! numbers, which is the same split the sorted set makes rather than a second
84//! design.
85//!
86//! # What a key is
87//!
88//! [`Key`] is the value at the path with a tag byte in front of it, so a
89//! document with the string `"7"` at a path and one with the number seven do
90//! not land on the same key. Every key is written so that comparing two of them
91//! as bytes gives the same answer comparing the values would. Equality does not
92//! need that, but it means the tree can compare keys with `memcmp` and never
93//! decode one.
94//!
95//! There is one tag for numbers rather than one for integers and one for
96//! floats, because a range over a path holding both has to put them in one
97//! order, and two tags cannot. Every finite number is a mantissa times a power
98//! of two, so a numeric key is written as where its leading bit sits, which is
99//! `floor(log2(|v|)) + 1` and is called the place here, followed by the mantissa
100//! shifted up to the top of eight bytes. Two numbers with different places are
101//! ordered by the place alone, and two with the same place are ordered by the
102//! mantissa read from the leading bit down, which is what the shift lines up.
103//! The place is biased by 32768 so that the whole range a f64 can reach sorts as
104//! an unsigned number, and everything after the class byte is flipped for a
105//! negative, because a bigger magnitude there is a smaller number.
106//!
107//! The byte in front of the place is the class, which is one of negative
108//! infinity, negative, zero, positive, positive infinity and NaN. Those five
109//! that are not an ordinary finite value have no size worth writing, so they
110//! carry a place and a mantissa of zero and are ordered by the class alone. NaN
111//! sorts above everything rather than being refused, so a range never has to
112//! think about it.
113//!
114//! An integer and a float that names the same integer, `7` and `7.0`, get the
115//! same key, because both normalise to a leading `111` and a place of three. A
116//! caller asking for seven means seven, and JSON has one number type, so the
117//! alternative is a query that misses documents for a reason nobody can see.
118//!
119//! Types do not interleave either: everything with a smaller tag sorts before
120//! everything with a larger one, so nulls, then booleans, then numbers, then
121//! strings. That one is on purpose. A range over a path is a range over one
122//! type, and a total order across types has to pick an arbitrary answer to
123//! whether a string is above or below a number.
124//!
125//! # More than one key at a time
126//!
127//! An array index files a document under every element of the array at the
128//! path, and a text index under every word of the string. Both answer the same
129//! question an equality index does, so [`PathIndex::find`] and
130//! [`PathIndex::count`] do not know which kind they are on, and the only thing
131//! that changes is how many keys a document has.
132//!
133//! A scalar at the path of an array index is an array of one. A collection
134//! where some documents carry a list of tags and some carry a single tag is a
135//! real collection, and an index that filed one and not the other would miss
136//! documents for a reason nobody can see.
137//!
138//! A text index folds case, so a search has to fold it too, and [`Key::word`]
139//! is what does that on the query side. Everything that is not a letter or a
140//! digit is a separator. That is a word index and not a search engine: there is
141//! no ranking, no stemming and no phrase matching, and the ranking that belongs
142//! on top of it is `10`. Splitting on bytes is also wrong for a language that
143//! does not put spaces between words, and the answer there is a real tokeniser
144//! rather than a rule here that is subtly wrong in another way.
145//!
146//! # What is not indexed
147//!
148//! A path that lands on an object or an array puts nothing in an equality
149//! index, and a document that has no value at the path puts nothing in any
150//! kind. Both are absences rather than errors: an index answers which documents
151//! have a given value there, and neither of those documents does.
152//!
153//! A path that lands on something other than a string puts nothing in a text
154//! index. A number has no words in it, and filing `7` under the key `7` in a
155//! text index would make one kind quietly behave like another.
156
157use core::cmp::Ordering;
158use core::ops::Bound;
159
160use yo_common::num::i64_digits;
161use yo_common::small::Small;
162use yo_common::{Code, Error, Result};
163use yo_kv::{Elements, Rank, Set, SetLimits, Slab, rank};
164
165use crate::head::Kind;
166use crate::read::Value;
167
168/// The longest an index key may be, which is the longest name an element table
169/// takes.
170///
171/// A text value past this cannot be filed, and a write that would have to file
172/// one fails rather than storing a document the index will never find. A silent
173/// absence from an index is a query that returns the wrong answer with no way
174/// to tell, and that is worse than a write that says no.
175pub const KEY_MAX: usize = yo_kv::NAME_MAX - 1;
176
177/// How much of a key sits in the caller's frame before it needs the allocator.
178///
179/// Twelve bytes covers every number, one byte covers a boolean or a null, and a
180/// tag and thirty one bytes covers the short strings that get indexed in
181/// practice: a status, a country, an identifier.
182const KEY_INLINE: usize = 32;
183
184const TAG_NULL: u8 = 0;
185const TAG_FALSE: u8 = 1;
186const TAG_TRUE: u8 = 2;
187const TAG_NUM: u8 = 3;
188const TAG_TEXT: u8 = 4;
189
190/// A value as an index looks it up.
191///
192/// Built from what the caller is searching for, or from what was found at a
193/// path in a document being written. The two go through the same code on
194/// purpose, because a query that encodes its argument differently from the way
195/// the write encoded the document is a query that finds nothing and says
196/// nothing about why.
197#[derive(Clone)]
198pub struct Key(Small<u8, KEY_INLINE>);
199
200impl Key {
201    /// The key for `null`.
202    #[must_use]
203    pub fn null() -> Key {
204        Key(Small::collect([TAG_NULL]))
205    }
206
207    /// The key for a boolean.
208    #[must_use]
209    pub fn bool(v: bool) -> Key {
210        Key(Small::collect([if v { TAG_TRUE } else { TAG_FALSE }]))
211    }
212
213    /// The key for an integer.
214    #[must_use]
215    pub fn int(v: i64) -> Key {
216        let (neg, mant) = if v < 0 {
217            (true, v.unsigned_abs())
218        } else {
219            (false, v as u64)
220        };
221        number(Class::of(neg, mant == 0), mant, i32::from(bits(mant)))
222    }
223
224    /// The key for a float.
225    ///
226    /// A float and an integer that name the same number get the same key, so
227    /// `7.0` and `7` are one key and a search for either finds both.
228    #[must_use]
229    pub fn float(v: f64) -> Key {
230        if v.is_nan() {
231            return number(Class::Nan, 0, 0);
232        }
233        if v.is_infinite() {
234            return number(
235                if v.is_sign_negative() {
236                    Class::NegInf
237                } else {
238                    Class::PosInf
239                },
240                0,
241                0,
242            );
243        }
244        let raw = v.to_bits();
245        let neg = raw >> 63 == 1;
246        let exponent = ((raw >> 52) & 0x7ff) as i32;
247        let fraction = raw & ((1 << 52) - 1);
248        // A subnormal has no implied leading one and a fixed exponent, and
249        // everything else has both.
250        let (mant, scale) = if exponent == 0 {
251            (fraction, -1074)
252        } else {
253            (fraction | (1 << 52), exponent - 1075)
254        };
255        number(
256            Class::of(neg, mant == 0),
257            mant,
258            scale + i32::from(bits(mant)),
259        )
260    }
261
262    /// The key for a string.
263    #[must_use]
264    pub fn text(v: &str) -> Key {
265        Key::text_bytes(v.as_bytes())
266    }
267
268    /// The key for a string that is already bytes.
269    #[must_use]
270    pub fn text_bytes(v: &[u8]) -> Key {
271        let mut k = Small::collect([TAG_TEXT]);
272        for &b in v {
273            k.push(b);
274        }
275        Key(k)
276    }
277
278    /// The key one word is filed under in a text index, or `None` if this is
279    /// not one word.
280    ///
281    /// A search against a text index goes through this rather than
282    /// [`Key::text`], because a text index folds case when it files a document
283    /// and a search that does not fold it finds nothing and says nothing about
284    /// why. Anything that is not letters and digits is a separator, so a phrase
285    /// is two words and answers `None`: matching one is a search this index
286    /// cannot answer on its own, rather than a search for the first word.
287    #[must_use]
288    pub fn word(v: &str) -> Option<Key> {
289        let mut rest = v.as_bytes();
290        let word = next_word(&mut rest)?;
291        if next_word(&mut rest).is_some() {
292            return None;
293        }
294        Some(fold(word))
295    }
296
297    /// The key for a value found in a document, or `None` if it is a container
298    /// and so has no equality key.
299    #[must_use]
300    pub fn of(v: Value<'_>) -> Option<Key> {
301        match v.kind() {
302            Kind::Null => Some(Key::null()),
303            Kind::Bool => Some(Key::bool(v.as_bool()?)),
304            Kind::Int => Some(Key::int(v.as_int()?)),
305            Kind::Float => Some(Key::float(v.as_float()?)),
306            Kind::Text => Some(Key::text_bytes(v.text_bytes()?)),
307            Kind::Array | Kind::Object => None,
308        }
309    }
310
311    /// The bytes this is filed under.
312    #[must_use]
313    pub fn as_bytes(&self) -> &[u8] {
314        self.0.as_slice()
315    }
316
317    /// Whether this key is too long to file.
318    #[must_use]
319    pub fn is_too_long(&self) -> bool {
320        self.as_bytes().len() > KEY_MAX
321    }
322}
323
324impl PartialEq for Key {
325    fn eq(&self, other: &Key) -> bool {
326        self.as_bytes() == other.as_bytes()
327    }
328}
329
330impl Eq for Key {}
331
332impl core::fmt::Debug for Key {
333    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
334        let b = self.as_bytes();
335        match b.first() {
336            Some(&TAG_NULL) => f.write_str("null"),
337            Some(&TAG_FALSE) => f.write_str("false"),
338            Some(&TAG_TRUE) => f.write_str("true"),
339            Some(&TAG_TEXT) => write!(f, "{:?}", String::from_utf8_lossy(&b[1..])),
340            Some(&TAG_NUM) => write!(f, "{}", Hex(&b[1..])),
341            _ => f.write_str("<no key>"),
342        }
343    }
344}
345
346/// Bytes as hex, for the numbers a key holds in an order preserving form that
347/// is not worth decoding back just to print it.
348struct Hex<'a>(&'a [u8]);
349
350impl core::fmt::Display for Hex<'_> {
351    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
352        for b in self.0 {
353            write!(f, "{b:02x}")?;
354        }
355        Ok(())
356    }
357}
358
359/// Where a number sits in the order, before its size is looked at.
360///
361/// The class is the first byte of a numeric key, so the five kinds of number
362/// that are not an ordinary finite value each land somewhere fixed rather than
363/// being encoded into the same bytes the finite ones use.
364#[derive(Debug, Clone, Copy, PartialEq, Eq)]
365enum Class {
366    NegInf = 0,
367    Negative = 1,
368    Zero = 2,
369    Positive = 3,
370    PosInf = 4,
371    /// Not a number, which JSON has no way to write and a document can only get
372    /// from a program that put one there. It sorts above everything rather than
373    /// being refused, so a range never has to think about it.
374    Nan = 5,
375}
376
377impl Class {
378    fn of(neg: bool, zero: bool) -> Class {
379        match (zero, neg) {
380            (true, _) => Class::Zero,
381            (false, true) => Class::Negative,
382            (false, false) => Class::Positive,
383        }
384    }
385}
386
387/// How many bits a magnitude takes.
388fn bits(mant: u64) -> u16 {
389    (64 - mant.leading_zeros()) as u16
390}
391
392/// A number as bytes that sort the way the number does, whether it arrived as
393/// an integer or as a float.
394///
395/// Every finite number is `mantissa * 2^k` for some odd mantissa, so `place` is
396/// where its leading bit sits, which is `floor(log2(|v|)) + 1`. Two numbers with
397/// different `place` are ordered by it alone, and two with the same `place` are
398/// ordered by their mantissas read from the leading bit down. Lining the
399/// mantissa up to the top of eight bytes is what makes that a byte comparison,
400/// and it is also what makes `7` and `7.0` the same bytes: both normalise to a
401/// leading `111` and a `place` of three, whatever they looked like on the way
402/// in.
403///
404/// Negatives get the ten bytes after the class flipped, because a bigger
405/// magnitude is a smaller number.
406fn number(class: Class, mant: u64, place: i32) -> Key {
407    let mut k = Small::collect([TAG_NUM, class as u8]);
408    let (place, mant) = match class {
409        // The size of an infinity, a zero or a NaN is not a question, and
410        // writing it as zero keeps every numeric key the same width.
411        Class::Negative | Class::Positive => (place, mant << mant.leading_zeros()),
412        _ => (0, 0),
413    };
414    // Biased so that the whole range a f64 can reach, which is roughly -1074 to
415    // 1025, is an unsigned number that sorts the way the signed one does.
416    let place = ((place + 32768) as u16).to_be_bytes();
417    let flip = if class == Class::Negative { 0xff } else { 0 };
418    for b in place.into_iter().chain(mant.to_be_bytes()) {
419        k.push(b ^ flip);
420    }
421    Key(k)
422}
423
424/// A float as bytes that sort the way the float does.
425/// What an index can be asked, and how many keys a document gets at its path.
426#[derive(Debug, Clone, Copy, PartialEq, Eq)]
427pub enum IndexKind {
428    /// One value at a time. A table from key to posting list and nothing else.
429    Equality,
430    /// One value at a time, or every value between two of them. The same table
431    /// with a counted B+ tree over its rows.
432    Ordered,
433    /// One element of an array at a time. A document with `["red", "blue"]` at
434    /// the path is filed under both, so a search for either finds it.
435    Array,
436    /// One word of a string at a time, folded to lower case. A document with
437    /// `"A red bicycle"` at the path is filed under `a`, `red` and `bicycle`.
438    Text,
439}
440
441impl IndexKind {
442    /// Whether this kind can be asked for a range as well as for a value.
443    #[must_use]
444    pub fn is_ordered(self) -> bool {
445        self == IndexKind::Ordered
446    }
447
448    /// Whether a document can be filed under more than one key at a time.
449    #[must_use]
450    pub fn is_multi(self) -> bool {
451        matches!(self, IndexKind::Array | IndexKind::Text)
452    }
453}
454
455/// A value at an indexed path that cannot be a key, because it is longer than
456/// [`KEY_MAX`].
457///
458/// Carried back rather than turned into an error here, so the layer that knows
459/// which path and which document it was can say so.
460#[derive(Debug, Clone, Copy)]
461pub(crate) struct TooLong;
462
463/// Append every key `at` files under, as a list of one length byte pair and
464/// then that many bytes.
465///
466/// Length prefixed rather than one buffer per key, because an array index files
467/// a document under as many keys as the array is long and a write is not
468/// allowed to allocate per element.
469///
470/// A path that lands on nothing this kind can use puts nothing in the list.
471/// That is an absence and not an error: an index answers which documents have a
472/// given value at the path, and a document with an object there does not have
473/// one.
474pub(crate) fn keys_at(
475    kind: IndexKind,
476    at: Value<'_>,
477    out: &mut Vec<u8>,
478) -> core::result::Result<(), TooLong> {
479    match kind {
480        IndexKind::Equality | IndexKind::Ordered => {
481            if let Some(key) = Key::of(at) {
482                push_key(&key, out)?;
483            }
484        }
485        IndexKind::Array => match at.kind() {
486            // A scalar at the path is an array of one. A caller that files
487            // `["red"]` on one document and `"red"` on the next means the same
488            // thing by both, and an index that disagreed would be a query that
489            // misses documents for a reason nobody can see.
490            Kind::Array => {
491                for elem in at.iter() {
492                    if let Some(key) = Key::of(elem) {
493                        push_key(&key, out)?;
494                    }
495                }
496            }
497            Kind::Object => {}
498            _ => {
499                if let Some(key) = Key::of(at) {
500                    push_key(&key, out)?;
501                }
502            }
503        },
504        IndexKind::Text => {
505            if let Some(text) = at.text_bytes() {
506                let mut rest = text;
507                while let Some(word) = next_word(&mut rest) {
508                    push_key(&fold(word), out)?;
509                }
510            }
511        }
512    }
513    Ok(())
514}
515
516/// Put one key on the end of a key list.
517fn push_key(key: &Key, out: &mut Vec<u8>) -> core::result::Result<(), TooLong> {
518    let bytes = key.as_bytes();
519    if key.is_too_long() {
520        return Err(TooLong);
521    }
522    // The length fits two bytes because KEY_MAX does, and the check above ran.
523    let n = bytes.len() as u16;
524    out.extend_from_slice(&n.to_le_bytes());
525    out.extend_from_slice(bytes);
526    Ok(())
527}
528
529/// Walk a key list back out again.
530pub(crate) fn each_key(mut list: &[u8], mut f: impl FnMut(&[u8])) {
531    while list.len() >= 2 {
532        let n = usize::from(u16::from_le_bytes([list[0], list[1]]));
533        let Some(key) = list.get(2..2 + n) else {
534            return;
535        };
536        f(key);
537        list = &list[2 + n..];
538    }
539}
540
541/// The next run of letters and digits in `rest`, with `rest` left after it.
542///
543/// Everything else is a separator, so punctuation, spaces and the bytes of a
544/// multi byte character all split. Splitting inside a word of a language that
545/// does not use spaces is wrong, and a real tokeniser is the answer rather than
546/// a rule here that is subtly wrong in a different way, so `10` will bring one.
547/// For the ASCII text that gets a text index today this is what a caller means.
548/// One word as the key a text index files it under, folded to lower case.
549fn fold(word: &[u8]) -> Key {
550    Key(Small::collect(
551        core::iter::once(TAG_TEXT).chain(word.iter().map(u8::to_ascii_lowercase)),
552    ))
553}
554
555fn next_word<'a>(rest: &mut &'a [u8]) -> Option<&'a [u8]> {
556    let start = rest.iter().position(|b| b.is_ascii_alphanumeric())?;
557    let after = rest[start..]
558        .iter()
559        .position(|b| !b.is_ascii_alphanumeric())
560        .map_or(rest.len(), |n| start + n);
561    let word = &rest[start..after];
562    *rest = &rest[after..];
563    Some(word)
564}
565
566/// One index, over one path.
567#[derive(Debug)]
568pub struct PathIndex {
569    /// The path as it was written, kept whole so it can be parsed again per
570    /// lookup. Parsing is a scan of a dozen bytes and it saves an owned step
571    /// type that would have to be kept in step with [`crate::Steps`].
572    path: Box<[u8]>,
573    /// What this index can be asked and how many keys a document gets.
574    kind: IndexKind,
575    /// The key to the slab slot its posting list sits in.
576    keys: Elements<u32>,
577    /// The rows of `keys` in key order, for an ordered index, and nothing at all
578    /// for an equality one.
579    ///
580    /// The table above is unordered, because it is a hash's field table. The
581    /// order lives here instead of being a property of the table, which is the
582    /// same split a sorted set makes: the members are in an element table and
583    /// the rank is a separate tree over its row numbers.
584    order: Option<Rank>,
585    /// The posting lists. A slab rather than a payload beside the row, because
586    /// [`Elements`] moves its last row into the hole on a removal and a posting
587    /// list is not `Copy`.
588    posts: Slab<Set>,
589    /// How many document ids are filed altogether, over every key.
590    postings: usize,
591}
592
593impl PathIndex {
594    /// An empty index over `path`, which has already been checked to parse.
595    pub(crate) fn new(path: &[u8], kind: IndexKind) -> PathIndex {
596        PathIndex {
597            path: path.into(),
598            kind,
599            keys: Elements::new(),
600            order: kind.is_ordered().then(Rank::new),
601            posts: Slab::new(),
602            postings: 0,
603        }
604    }
605
606    /// The path this indexes.
607    #[must_use]
608    pub fn path(&self) -> &[u8] {
609        &self.path
610    }
611
612    /// What this index can be asked.
613    #[must_use]
614    pub fn kind(&self) -> IndexKind {
615        self.kind
616    }
617
618    /// Every key `at` files under in this index.
619    pub(crate) fn keys_at(
620        &self,
621        at: Value<'_>,
622        out: &mut Vec<u8>,
623    ) -> core::result::Result<(), TooLong> {
624        keys_at(self.kind, at, out)
625    }
626
627    /// How many distinct values are filed.
628    #[must_use]
629    pub fn len(&self) -> usize {
630        self.keys.len()
631    }
632
633    /// Whether nothing is filed.
634    #[must_use]
635    pub fn is_empty(&self) -> bool {
636        self.keys.is_empty()
637    }
638
639    /// How many document ids are filed altogether.
640    ///
641    /// One per document that has a scalar at this path, so the difference
642    /// between this and the collection's length is how many documents the index
643    /// does not cover.
644    #[must_use]
645    pub fn postings(&self) -> usize {
646        self.postings
647    }
648
649    /// The documents filed under `key`.
650    ///
651    /// A [`Set`], so it can be intersected with another one by the same code
652    /// `SINTER` uses.
653    #[must_use]
654    pub fn get(&self, key: &Key) -> Option<&Set> {
655        self.posts.get(*self.keys.get(key.as_bytes())?)
656    }
657
658    /// How many documents are filed under `key`.
659    ///
660    /// The number a query planner sorts its filters by, and it is a probe
661    /// rather than a walk.
662    #[must_use]
663    pub fn count(&self, key: &Key) -> usize {
664        self.get(key).map_or(0, Set::len)
665    }
666
667    /// Every key between `lo` and `hi` with the documents filed under it, in
668    /// order.
669    ///
670    /// One descent of the tree and then a link per leaf, so a range of a
671    /// thousand keys costs one search and a handful of hops. An equality index
672    /// has no order to walk and answers nothing at all rather than pretending to
673    /// have a range; the layer above turns that into an error, because a range
674    /// query that silently finds nothing is worse than one that says no.
675    #[must_use]
676    pub fn range(&self, lo: Bound<&Key>, hi: Bound<&Key>) -> Ranged<'_> {
677        let Some((order, start, left)) = self.span(lo, hi) else {
678            return Ranged {
679                index: self,
680                walk: None,
681                left: 0,
682            };
683        };
684        Ranged {
685            index: self,
686            walk: Some(order.iter_from(start)),
687            left,
688        }
689    }
690
691    /// [`PathIndex::range`] backwards, largest key first.
692    #[must_use]
693    pub fn range_rev(&self, lo: Bound<&Key>, hi: Bound<&Key>) -> RangedRev<'_> {
694        let Some((order, start, left)) = self.span(lo, hi) else {
695            return RangedRev {
696                index: self,
697                walk: None,
698                left: 0,
699            };
700        };
701        RangedRev {
702            index: self,
703            walk: Some(order.iter_back_from(start + left - 1)),
704            left,
705        }
706    }
707
708    /// How many documents are filed under any key between `lo` and `hi`.
709    ///
710    /// This reads the keys in the range and not the documents, so it costs the
711    /// number of distinct values rather than the number of postings.
712    #[must_use]
713    pub fn count_in(&self, lo: Bound<&Key>, hi: Bound<&Key>) -> usize {
714        self.range(lo, hi).map(|(_, set)| set.len()).sum()
715    }
716
717    /// Where a range starts and how many keys are in it, or `None` if there is
718    /// no order to walk or nothing in the range.
719    fn span(&self, lo: Bound<&Key>, hi: Bound<&Key>) -> Option<(&Rank, usize, usize)> {
720        let order = self.order.as_ref()?;
721        let keys = &self.keys;
722        let start = match lo {
723            Bound::Unbounded => 0,
724            Bound::Included(k) => rank_of(order, keys, k.as_bytes()),
725            Bound::Excluded(k) => rank_after(order, keys, k.as_bytes()),
726        };
727        let end = match hi {
728            Bound::Unbounded => keys.len(),
729            Bound::Included(k) => rank_after(order, keys, k.as_bytes()),
730            Bound::Excluded(k) => rank_of(order, keys, k.as_bytes()),
731        };
732        if end <= start {
733            return None;
734        }
735        Some((order, start, end - start))
736    }
737
738    /// File `id` under `key`.
739    pub(crate) fn add(&mut self, key: &[u8], id: &[u8]) -> Result<()> {
740        if let Some(&slot) = self.keys.get(key) {
741            let set = self.posts.get_mut(slot).expect("a row points at its list");
742            if set.add(id, &SetLimits::DEFAULT) {
743                self.postings += 1;
744            }
745            return Ok(());
746        }
747        let mut set = Set::new();
748        set.add(id, &SetLimits::DEFAULT);
749        let slot = self.posts.insert(set);
750        let row = self.keys.len() as u32;
751        if self.keys.insert(key, slot).is_err() {
752            self.posts.remove(slot);
753            return Err(Error::new(
754                Code::Full,
755                "the index cannot hold another distinct value",
756            ));
757        }
758        let PathIndex { keys, order, .. } = self;
759        if let Some(order) = order {
760            // The key is in the table already and not in the tree, so the search
761            // compares it against every other key and lands where it belongs.
762            let at = rank_of(order, keys, key);
763            order.insert_at(at, row);
764        }
765        self.postings += 1;
766        Ok(())
767    }
768
769    /// Take `id` out from under `key`, and drop the key if it was the last one.
770    pub(crate) fn take(&mut self, key: &[u8], id: &[u8]) {
771        let Some(row) = self.keys.index_of(key) else {
772            return;
773        };
774        let slot = *self.keys.at(row).expect("a row that was just found").1;
775        let set = self.posts.get_mut(slot).expect("a row points at its list");
776        if !set.remove(id) {
777            return;
778        }
779        self.postings -= 1;
780        if !set.is_empty() {
781            return;
782        }
783        self.posts.remove(slot);
784        self.untrack(key, row);
785        self.keys.remove_at(row);
786    }
787
788    /// Take `row` out of the tree, and tell the tree about the row the element
789    /// table is about to renumber.
790    ///
791    /// The table is dense, so taking a row out moves the last row into the hole
792    /// and one key nobody asked about gets a new number. Where that key sits has
793    /// to be found before anything moves, because afterwards the tree is holding
794    /// a number that means something else. This is the same dance a sorted set
795    /// does, for the same reason.
796    ///
797    /// An equality index has no tree and nothing to do here.
798    fn untrack(&mut self, key: &[u8], row: usize) {
799        let PathIndex { keys, order, .. } = self;
800        let Some(order) = order else {
801            return;
802        };
803        let rank = rank_of(order, keys, key);
804        let last = keys.len() - 1;
805        let moved = if last == row {
806            None
807        } else {
808            let name = keys.at(last).expect("the last row").0;
809            Some(order.seek(|other| {
810                let (other_name, _) = keys.at(other as usize).expect("a row the tree holds");
811                name.cmp(other_name)
812            }))
813        };
814        order.remove_at(rank);
815        if let Some(at) = moved {
816            // Everything above the hole shifted down by one when the row came
817            // out of the tree.
818            let at = if at > rank { at - 1 } else { at };
819            order.set_at(at, row as u32);
820        }
821    }
822
823    /// Throw everything filed away and keep the path and the kind.
824    pub(crate) fn clear(&mut self) {
825        self.keys.clear();
826        self.posts.clear();
827        self.postings = 0;
828        if let Some(order) = &mut self.order {
829            *order = Rank::new();
830        }
831    }
832
833    /// What the index costs, posting lists and the order included.
834    #[must_use]
835    pub fn memory_bytes(&self) -> usize {
836        self.keys.memory_bytes()
837            + self.posts.slot_bytes()
838            + self.posts.iter().map(Set::memory_bytes).sum::<usize>()
839            + self.order.as_ref().map_or(0, Rank::bytes)
840    }
841}
842
843/// Keys in order with their posting lists, from [`PathIndex::range`].
844pub struct Ranged<'a> {
845    index: &'a PathIndex,
846    walk: Option<rank::Walk<'a>>,
847    left: usize,
848}
849
850impl<'a> Iterator for Ranged<'a> {
851    type Item = (&'a [u8], &'a Set);
852
853    fn next(&mut self) -> Option<(&'a [u8], &'a Set)> {
854        if self.left == 0 {
855            return None;
856        }
857        let row = self.walk.as_mut()?.next()?;
858        self.left -= 1;
859        entry(self.index, row)
860    }
861
862    fn size_hint(&self) -> (usize, Option<usize>) {
863        (self.left, Some(self.left))
864    }
865}
866
867impl ExactSizeIterator for Ranged<'_> {}
868
869/// Keys in reverse order with their posting lists, from
870/// [`PathIndex::range_rev`].
871pub struct RangedRev<'a> {
872    index: &'a PathIndex,
873    walk: Option<rank::Back<'a>>,
874    left: usize,
875}
876
877impl<'a> Iterator for RangedRev<'a> {
878    type Item = (&'a [u8], &'a Set);
879
880    fn next(&mut self) -> Option<(&'a [u8], &'a Set)> {
881        if self.left == 0 {
882            return None;
883        }
884        let row = self.walk.as_mut()?.next()?;
885        self.left -= 1;
886        entry(self.index, row)
887    }
888
889    fn size_hint(&self) -> (usize, Option<usize>) {
890        (self.left, Some(self.left))
891    }
892}
893
894impl ExactSizeIterator for RangedRev<'_> {}
895
896/// The rank `key` sits at in `order`, or would sit at.
897///
898/// A free function rather than a method because every caller has the tree and
899/// the table split out of the index already, either because it is about to
900/// write to the tree while reading the table or because it is holding a borrow
901/// of the tree it means to keep.
902fn rank_of(order: &Rank, keys: &Elements<u32>, key: &[u8]) -> usize {
903    order.seek(|row| {
904        let (name, _) = keys.at(row as usize).expect("a row the tree holds");
905        key.cmp(name)
906    })
907}
908
909/// The rank one past `key`, which is where it sits when it is not there and one
910/// to the right of it when it is.
911fn rank_after(order: &Rank, keys: &Elements<u32>, key: &[u8]) -> usize {
912    order.seek(|row| {
913        let (name, _) = keys.at(row as usize).expect("a row the tree holds");
914        match key.cmp(name) {
915            Ordering::Less => Ordering::Less,
916            Ordering::Equal | Ordering::Greater => Ordering::Greater,
917        }
918    })
919}
920
921/// The key and the posting list a tree row names.
922fn entry(index: &PathIndex, row: u32) -> Option<(&[u8], &Set)> {
923    let (name, &slot) = index.keys.at(row as usize)?;
924    Some((name, index.posts.get(slot)?))
925}
926
927/// Hand every id in `set` to `f` as bytes.
928///
929/// A posting list of numeric ids is an intset, so the ids come back as integers
930/// and have to be written out again to be probed with. The digits go in a
931/// buffer on this frame, so a walk over a million postings allocates nothing.
932pub(crate) fn each_id(set: &Set, mut f: impl FnMut(&[u8])) -> usize {
933    let mut digits = [0u8; yo_common::num::DIGITS_MAX];
934    let mut n = 0usize;
935    for member in set.iter() {
936        match member {
937            yo_kv::listpack::Entry::Str(s) => f(s),
938            yo_kv::listpack::Entry::Int(v) => f(i64_digits(&mut digits, v)),
939        }
940        n += 1;
941    }
942    n
943}
944
945#[cfg(test)]
946mod tests {
947    use super::*;
948
949    /// The keys a kind takes from one value, as printable strings.
950    fn taken(kind: IndexKind, build: impl FnOnce(&mut crate::Builder)) -> Vec<String> {
951        let mut b = crate::Builder::new();
952        build(&mut b);
953        let bytes = b.finish().expect("built").to_vec();
954        let value = Value::new(&bytes).expect("readable");
955        let mut list = Vec::new();
956        keys_at(kind, value, &mut list).expect("short enough");
957        let mut out = Vec::new();
958        each_key(&list, |key| {
959            out.push(format!("{:?}", Key(Small::collect(key.iter().copied()))))
960        });
961        out
962    }
963
964    #[test]
965    fn an_array_index_takes_one_key_per_element() {
966        let keys = taken(IndexKind::Array, |b| {
967            b.begin_array().expect("open");
968            b.text("red").expect("value");
969            b.int(7).expect("value");
970            b.begin_object().expect("open");
971            b.end_object().expect("close");
972            b.end_array().expect("close");
973        });
974        assert_eq!(keys.len(), 2, "the object inside is not a key: {keys:?}");
975        assert_eq!(keys[0], "\"red\"");
976
977        // A scalar is a list of one, and an object is a list of none.
978        assert_eq!(
979            taken(IndexKind::Array, |b| b.text("red").expect("v")).len(),
980            1
981        );
982        assert_eq!(
983            taken(IndexKind::Array, |b| {
984                b.begin_object().expect("open");
985                b.end_object().expect("close");
986            })
987            .len(),
988            0
989        );
990    }
991
992    #[test]
993    fn a_text_index_splits_on_everything_that_is_not_a_letter_or_a_digit() {
994        let keys = taken(IndexKind::Text, |b| {
995            b.text("  The RED car, model 3! ").expect("value")
996        });
997        assert_eq!(
998            keys,
999            ["\"the\"", "\"red\"", "\"car\"", "\"model\"", "\"3\""]
1000        );
1001
1002        assert!(taken(IndexKind::Text, |b| b.text("!!! ...").expect("v")).is_empty());
1003        assert!(taken(IndexKind::Text, |b| b.int(7).expect("v")).is_empty());
1004    }
1005
1006    #[test]
1007    fn a_word_key_is_what_a_text_index_filed_and_a_phrase_is_not_one() {
1008        assert_eq!(Key::word("RED"), Key::word("red"));
1009        assert_eq!(Key::word("red!"), Key::word("red"));
1010        assert!(Key::word("red car").is_none(), "a phrase is two words");
1011        assert!(Key::word("").is_none());
1012        assert!(Key::word("!!!").is_none());
1013        assert_eq!(
1014            Key::word("red").expect("a word"),
1015            Key::text("red"),
1016            "a word that needs no folding is the string key, and there is no \
1017             second text tag to keep them apart"
1018        );
1019        assert_ne!(Key::word("RED").expect("a word"), Key::text("RED"));
1020    }
1021
1022    #[test]
1023    fn a_key_list_reads_back_exactly_what_went_into_it() {
1024        let mut list = Vec::new();
1025        push_key(&Key::text("red"), &mut list).expect("short");
1026        push_key(&Key::int(7), &mut list).expect("short");
1027        push_key(&Key::null(), &mut list).expect("short");
1028        let mut out = Vec::new();
1029        each_key(&list, |key| out.push(key.to_vec()));
1030        assert_eq!(
1031            out,
1032            [
1033                Key::text("red").as_bytes().to_vec(),
1034                Key::int(7).as_bytes().to_vec(),
1035                Key::null().as_bytes().to_vec(),
1036            ]
1037        );
1038
1039        let long = "x".repeat(KEY_MAX);
1040        assert!(push_key(&Key::text(&long), &mut list).is_err());
1041    }
1042
1043    #[test]
1044    fn a_number_and_the_string_of_it_are_different_keys() {
1045        assert_ne!(Key::int(7), Key::text("7"));
1046        assert_ne!(Key::null(), Key::text(""));
1047        assert_ne!(Key::bool(true), Key::int(1));
1048    }
1049
1050    #[test]
1051    fn a_float_that_names_a_whole_number_is_that_number() {
1052        assert_eq!(Key::float(7.0), Key::int(7));
1053        assert_eq!(Key::float(-0.0), Key::int(0));
1054        assert_eq!(Key::float(-3.0), Key::int(-3));
1055        assert_ne!(Key::float(7.5), Key::int(7));
1056        assert_ne!(Key::float(1e30), Key::int(i64::MAX));
1057        assert_ne!(Key::float(f64::NAN), Key::float(0.0));
1058    }
1059
1060    #[test]
1061    fn numbers_sort_as_bytes_the_way_they_sort_as_numbers() {
1062        let mut ns = [0i64, -1, i64::MIN, i64::MAX, 7, -7, 1 << 40];
1063        let mut keys: Vec<Key> = ns.iter().map(|&n| Key::int(n)).collect();
1064        ns.sort_unstable();
1065        keys.sort_by(|a, b| a.as_bytes().cmp(b.as_bytes()));
1066        let want: Vec<Key> = ns.iter().map(|&n| Key::int(n)).collect();
1067        assert_eq!(keys, want);
1068
1069        let mut fs = [0.5f64, -0.5, -1.5, 1e300, -1e300, f64::MIN_POSITIVE];
1070        let mut keys: Vec<Key> = fs.iter().map(|&f| Key::float(f)).collect();
1071        fs.sort_by(f64::total_cmp);
1072        keys.sort_by(|a, b| a.as_bytes().cmp(b.as_bytes()));
1073        let want: Vec<Key> = fs.iter().map(|&f| Key::float(f)).collect();
1074        assert_eq!(keys, want);
1075    }
1076
1077    #[test]
1078    fn an_integer_and_a_float_sort_among_each_other() {
1079        // The order this has to produce is the numeric one, and the two ways of
1080        // writing a number are mixed on purpose so that nothing can pass by
1081        // keeping the integers on one side and the floats on the other.
1082        let mut mixed: Vec<Key> = [
1083            Key::float(12.5),
1084            Key::int(99),
1085            Key::int(-3),
1086            Key::float(-2.5),
1087            Key::int(0),
1088            Key::float(0.25),
1089            Key::int(13),
1090        ]
1091        .to_vec();
1092        mixed.sort_by(|a, b| a.as_bytes().cmp(b.as_bytes()));
1093        let want = [
1094            Key::int(-3),
1095            Key::float(-2.5),
1096            Key::int(0),
1097            Key::float(0.25),
1098            Key::float(12.5),
1099            Key::int(13),
1100            Key::int(99),
1101        ];
1102        assert_eq!(mixed, want);
1103    }
1104
1105    #[test]
1106    fn seven_and_seven_point_zero_are_one_key() {
1107        assert_eq!(Key::int(7), Key::float(7.0));
1108        assert_eq!(Key::int(-7), Key::float(-7.0));
1109        assert_eq!(Key::int(0), Key::float(0.0));
1110        // A negative zero is a zero. Nothing else would let a caller who asks
1111        // for zero find a document that has one.
1112        assert_eq!(Key::int(0), Key::float(-0.0));
1113        assert_eq!(Key::int(1 << 53), Key::float((1u64 << 53) as f64));
1114        // And two numbers that are close are still two numbers. `i64::MAX` is
1115        // one below a power of two and the nearest f64 to it is that power of
1116        // two, so these are not the same value and do not get the same key.
1117        assert_ne!(Key::int(i64::MAX), Key::float(i64::MAX as f64));
1118    }
1119
1120    #[test]
1121    fn the_ends_of_the_number_line_sort_where_they_belong() {
1122        let mut ends = [
1123            Key::float(f64::NAN),
1124            Key::float(f64::INFINITY),
1125            Key::int(1),
1126            Key::float(f64::NEG_INFINITY),
1127            Key::int(-1),
1128            Key::float(f64::MIN),
1129            Key::float(f64::MAX),
1130        ]
1131        .to_vec();
1132        ends.sort_by(|a, b| a.as_bytes().cmp(b.as_bytes()));
1133        let want = [
1134            Key::float(f64::NEG_INFINITY),
1135            Key::float(f64::MIN),
1136            Key::int(-1),
1137            Key::int(1),
1138            Key::float(f64::MAX),
1139            Key::float(f64::INFINITY),
1140            // Above everything, so a range never has to think about it.
1141            Key::float(f64::NAN),
1142        ];
1143        assert_eq!(ends, want);
1144    }
1145
1146    #[test]
1147    fn every_number_is_the_same_width() {
1148        for k in [
1149            Key::int(0),
1150            Key::int(i64::MIN),
1151            Key::float(1e300),
1152            Key::float(f64::MIN_POSITIVE),
1153            Key::float(f64::NAN),
1154            Key::float(f64::NEG_INFINITY),
1155        ] {
1156            assert_eq!(k.as_bytes().len(), 12, "{k:?}");
1157        }
1158    }
1159
1160    #[test]
1161    fn a_short_key_stays_off_the_heap() {
1162        assert!(Key::int(i64::MIN).0.is_inline());
1163        assert!(Key::text("a-fairly-ordinary-status").0.is_inline());
1164        assert!(!Key::text(&"x".repeat(64)).0.is_inline());
1165    }
1166
1167    #[test]
1168    fn a_key_prints_as_what_it_is() {
1169        assert_eq!(format!("{:?}", Key::null()), "null");
1170        assert_eq!(format!("{:?}", Key::bool(true)), "true");
1171        assert_eq!(format!("{:?}", Key::text("open")), "\"open\"");
1172        // The class byte for a zero, then a place and a mantissa that are both
1173        // written as zero because the size of a zero is not a question.
1174        assert_eq!(format!("{:?}", Key::int(0)), "0280000000000000000000");
1175    }
1176
1177    /// An ordered index over `$.n` holding the integers given, one document per
1178    /// integer, named after it.
1179    fn ordered(ns: impl IntoIterator<Item = i64>) -> PathIndex {
1180        let mut index = PathIndex::new(b"$.n", IndexKind::Ordered);
1181        for n in ns {
1182            index
1183                .add(Key::int(n).as_bytes(), n.to_string().as_bytes())
1184                .expect("room");
1185        }
1186        index
1187    }
1188
1189    /// The keys a range walks, decoded back to the integers they came from.
1190    fn walked(index: &PathIndex, lo: Bound<&Key>, hi: Bound<&Key>) -> Vec<i64> {
1191        let out: Vec<i64> = index.range(lo, hi).map(|(k, _)| unorder_int(k)).collect();
1192        let mut back: Vec<i64> = index
1193            .range_rev(lo, hi)
1194            .map(|(k, _)| unorder_int(k))
1195            .collect();
1196        back.reverse();
1197        assert_eq!(out, back, "backwards is forwards read the other way");
1198        out
1199    }
1200
1201    /// The number a numeric key was made from, for the whole numbers these
1202    /// tests file.
1203    fn unorder_int(key: &[u8]) -> i64 {
1204        assert_eq!(key[0], TAG_NUM, "these tests only file numbers");
1205        let class = key[1];
1206        if class == Class::Zero as u8 {
1207            return 0;
1208        }
1209        let flip = if class == Class::Negative as u8 {
1210            0xffu8
1211        } else {
1212            0
1213        };
1214        let place = u16::from_be_bytes([key[2] ^ flip, key[3] ^ flip]) as i32 - 32768;
1215        let mut mant = [0u8; 8];
1216        for (out, b) in mant.iter_mut().zip(&key[4..12]) {
1217            *out = b ^ flip;
1218        }
1219        // The mantissa sits at the top of the eight bytes, so shifting it back
1220        // down by however far its leading bit is from `place` gives the integer.
1221        let n = (u64::from_be_bytes(mant) >> (64 - place)) as i64;
1222        if flip == 0 { n } else { -n }
1223    }
1224
1225    #[test]
1226    fn an_ordered_index_walks_its_keys_in_order() {
1227        // Written in an order that is neither sorted nor reverse sorted, and
1228        // over enough keys to push the tree past one leaf.
1229        let index = ordered((0..500i64).map(|i| (i * 137) % 500 - 250));
1230        assert_eq!(index.len(), 500);
1231        assert_eq!(index.kind(), IndexKind::Ordered);
1232
1233        let all = walked(&index, Bound::Unbounded, Bound::Unbounded);
1234        assert_eq!(all, (-250..250).collect::<Vec<i64>>());
1235
1236        let (lo, hi) = (Key::int(-3), Key::int(4));
1237        assert_eq!(
1238            walked(&index, Bound::Included(&lo), Bound::Excluded(&hi)),
1239            [-3, -2, -1, 0, 1, 2, 3]
1240        );
1241        assert_eq!(
1242            walked(&index, Bound::Excluded(&lo), Bound::Included(&hi)),
1243            [-2, -1, 0, 1, 2, 3, 4]
1244        );
1245        assert_eq!(
1246            walked(&index, Bound::Unbounded, Bound::Excluded(&Key::int(-247))),
1247            [-250, -249, -248]
1248        );
1249        assert_eq!(
1250            walked(&index, Bound::Included(&Key::int(247)), Bound::Unbounded),
1251            [247, 248, 249]
1252        );
1253    }
1254
1255    #[test]
1256    fn a_range_that_names_nothing_is_empty_rather_than_wrong() {
1257        let index = ordered([10i64, 20, 30]);
1258        let (lo, hi) = (Key::int(20), Key::int(20));
1259        assert!(walked(&index, Bound::Excluded(&lo), Bound::Excluded(&hi)).is_empty());
1260        assert_eq!(
1261            walked(&index, Bound::Included(&lo), Bound::Included(&hi)),
1262            [20]
1263        );
1264        // Backwards bounds, which a caller can hand over by accident.
1265        assert!(
1266            walked(
1267                &index,
1268                Bound::Included(&Key::int(30)),
1269                Bound::Excluded(&Key::int(10))
1270            )
1271            .is_empty()
1272        );
1273        // Between two keys that are there, and past both ends.
1274        assert!(
1275            walked(
1276                &index,
1277                Bound::Included(&Key::int(21)),
1278                Bound::Excluded(&Key::int(29))
1279            )
1280            .is_empty()
1281        );
1282        assert!(walked(&index, Bound::Included(&Key::int(31)), Bound::Unbounded).is_empty());
1283        assert!(walked(&index, Bound::Unbounded, Bound::Excluded(&Key::int(10))).is_empty());
1284        assert_eq!(index.count_in(Bound::Unbounded, Bound::Unbounded), 3);
1285    }
1286
1287    #[test]
1288    fn an_equality_index_has_no_range_and_says_so_by_being_empty() {
1289        let mut index = PathIndex::new(b"$.n", IndexKind::Equality);
1290        index.add(Key::int(1).as_bytes(), b"a").expect("room");
1291        assert_eq!(index.kind(), IndexKind::Equality);
1292        assert_eq!(index.range(Bound::Unbounded, Bound::Unbounded).count(), 0);
1293        assert_eq!(index.count_in(Bound::Unbounded, Bound::Unbounded), 0);
1294        assert_eq!(index.count(&Key::int(1)), 1, "equality still works");
1295    }
1296
1297    #[test]
1298    fn removing_keys_from_an_ordered_index_keeps_the_rest_in_order() {
1299        // Every removal moves the element table's last row into the hole, so the
1300        // tree is holding a row number that has come to mean a different key.
1301        // This is the test that the renumbering is told to it.
1302        let mut index = ordered(0..200i64);
1303        for n in (0..200i64).step_by(3) {
1304            index.take(Key::int(n).as_bytes(), n.to_string().as_bytes());
1305        }
1306        let left: Vec<i64> = (0..200i64).filter(|n| n % 3 != 0).collect();
1307        assert_eq!(index.len(), left.len());
1308        assert_eq!(walked(&index, Bound::Unbounded, Bound::Unbounded), left);
1309
1310        // And the keys still find their own posting lists after all that.
1311        for n in &left {
1312            assert_eq!(index.count(&Key::int(*n)), 1, "{n} lost its list");
1313        }
1314        for n in (0..200i64).step_by(3) {
1315            assert_eq!(index.count(&Key::int(n)), 0, "{n} kept one");
1316        }
1317    }
1318
1319    #[test]
1320    fn an_ordered_index_that_is_emptied_and_refilled_is_still_ordered() {
1321        let mut index = ordered(0..64i64);
1322        for n in 0..64i64 {
1323            index.take(Key::int(n).as_bytes(), n.to_string().as_bytes());
1324        }
1325        assert!(index.is_empty());
1326        assert_eq!(index.postings(), 0);
1327        assert!(walked(&index, Bound::Unbounded, Bound::Unbounded).is_empty());
1328
1329        for n in (0..32i64).rev() {
1330            index
1331                .add(Key::int(n).as_bytes(), n.to_string().as_bytes())
1332                .expect("room");
1333        }
1334        assert_eq!(
1335            walked(&index, Bound::Unbounded, Bound::Unbounded),
1336            (0..32).collect::<Vec<i64>>()
1337        );
1338
1339        index.clear();
1340        assert_eq!(index.kind(), IndexKind::Ordered, "a clear keeps the kind");
1341        assert!(index.is_empty());
1342        index.add(Key::int(9).as_bytes(), b"9").expect("room");
1343        assert_eq!(walked(&index, Bound::Unbounded, Bound::Unbounded), [9]);
1344    }
1345
1346    #[test]
1347    fn a_key_with_many_documents_counts_once_in_the_order() {
1348        let mut index = PathIndex::new(b"$.n", IndexKind::Ordered);
1349        for i in 0..100 {
1350            index
1351                .add(
1352                    Key::int(i64::from(i % 5)).as_bytes(),
1353                    format!("d{i}").as_bytes(),
1354                )
1355                .expect("room");
1356        }
1357        assert_eq!(index.len(), 5, "five distinct values");
1358        assert_eq!(index.postings(), 100);
1359        assert_eq!(
1360            walked(&index, Bound::Unbounded, Bound::Unbounded),
1361            [0, 1, 2, 3, 4]
1362        );
1363        assert_eq!(index.count_in(Bound::Unbounded, Bound::Unbounded), 100);
1364        assert_eq!(
1365            index.count_in(Bound::Included(&Key::int(1)), Bound::Included(&Key::int(2))),
1366            40
1367        );
1368    }
1369
1370    #[test]
1371    fn the_last_document_under_a_key_takes_the_key_with_it() {
1372        let mut index = PathIndex::new(b"$.status", IndexKind::Equality);
1373        let open = Key::text("open");
1374        index.add(open.as_bytes(), b"a").expect("room");
1375        index.add(open.as_bytes(), b"b").expect("room");
1376        assert_eq!(index.len(), 1);
1377        assert_eq!(index.postings(), 2);
1378        assert_eq!(index.count(&open), 2);
1379
1380        index.take(open.as_bytes(), b"a");
1381        assert_eq!(index.postings(), 1);
1382        assert_eq!(index.len(), 1);
1383        index.take(open.as_bytes(), b"b");
1384        assert_eq!(index.postings(), 0);
1385        assert!(index.is_empty(), "an empty posting list is not a key");
1386        assert_eq!(index.count(&open), 0);
1387    }
1388
1389    #[test]
1390    fn filing_the_same_document_twice_files_it_once() {
1391        let mut index = PathIndex::new(b"$.status", IndexKind::Equality);
1392        let open = Key::text("open");
1393        index.add(open.as_bytes(), b"a").expect("room");
1394        index.add(open.as_bytes(), b"a").expect("room");
1395        assert_eq!(index.postings(), 1);
1396        index.take(open.as_bytes(), b"a");
1397        assert_eq!(index.postings(), 0);
1398    }
1399
1400    #[test]
1401    fn taking_out_something_that_was_never_filed_changes_nothing() {
1402        let mut index = PathIndex::new(b"$.status", IndexKind::Equality);
1403        let open = Key::text("open");
1404        index.add(open.as_bytes(), b"a").expect("room");
1405        index.take(open.as_bytes(), b"never");
1406        index.take(Key::text("shut").as_bytes(), b"a");
1407        assert_eq!(index.postings(), 1);
1408        assert_eq!(index.count(&open), 1);
1409    }
1410
1411    #[test]
1412    fn a_posting_list_of_numbers_reads_back_as_bytes() {
1413        let mut index = PathIndex::new(b"$.customer", IndexKind::Equality);
1414        let key = Key::int(4);
1415        for id in ["11", "2", "333"] {
1416            index.add(key.as_bytes(), id.as_bytes()).expect("room");
1417        }
1418        let mut got = Vec::new();
1419        let n = each_id(index.get(&key).expect("filed"), |id| {
1420            got.push(String::from_utf8_lossy(id).into_owned());
1421        });
1422        assert_eq!(n, 3);
1423        got.sort();
1424        assert_eq!(got, ["11", "2", "333"]);
1425    }
1426}