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