Skip to main content

yo_doc/
docs.rs

1//! A document collection: the primary table and the key table that goes with
2//! it (`09` section 4).
3//!
4//! The primary table is an element table keyed by document id with the
5//! document's bytes stored behind its id in the same blob. That is not a family
6//! resemblance to a hash's field table, it is the same code: [`Elements`] in
7//! tailed mode, which is what `HSET` writes into. A document collection and a
8//! hash differ in what the bytes behind the name mean and in nothing else, and
9//! that is the point of R25.
10//!
11//! ```
12//! use yo_doc::{Builder, Docs};
13//!
14//! let mut b = Builder::new();
15//! b.begin_object()?;
16//! b.key(b"customer")?;
17//! b.int(7)?;
18//! b.key(b"status")?;
19//! b.text("open")?;
20//! b.end_object()?;
21//! let order = b.finish()?.to_vec();
22//!
23//! let mut docs = Docs::new();
24//! assert!(docs.put_bytes(b"order:1", &order)?);
25//! let got = docs.get(b"order:1").expect("stored");
26//! assert_eq!(got.get(b"status").and_then(|v| v.as_text()), Some("open"));
27//! # Ok::<(), yo_common::Error>(())
28//! ```
29//!
30//! # What a write does
31//!
32//! [`Docs::put`] does not store the value it is given. It walks it once and
33//! writes it again with every object key replaced by its id from the
34//! collection's [`Keys`], which is where the forty percent that interning is
35//! worth actually gets saved.
36//!
37//! If the key table fills part way through, the document is stored as it
38//! arrived with its keys as bytes. Nothing about that is a fallback mode: the
39//! interned flag sits in each container's header, so a collection holds both
40//! kinds at once, a reader tells them apart per container, and documents
41//! written before the table filled stay exactly as they were.
42//!
43//! # What a write does about the indexes
44//!
45//! A write takes one path lookup per declared index, not a comparison against
46//! every index path at every node of the document. I had it the other way round
47//! at first, on the argument that the interning walk is already touching every
48//! node so the extraction may as well ride along on it. That is worse: with N
49//! indexes it costs N comparisons at every node, where a lookup per index costs
50//! N times the two or three binary searches a shallow path takes, and index
51//! paths are shallow. It is also much simpler, and it is the same code the
52//! backfill in [`Docs::create_index`] runs.
53//!
54//! The keys are worked out before anything is stored, so a value that is too
55//! long to be an index key fails the write rather than leaving behind a document
56//! that is in the collection and in none of its indexes. Then the old document
57//! under that id is taken out of the indexes, then the new one is stored, then
58//! it is filed. An overwrite and a removal both un-index through the same code,
59//! because both make the old entries wrong.
60//!
61//! # Reading one back
62//!
63//! [`Docs::get`] answers a [`Doc`], which is a [`Value`] with the collection's
64//! key table beside it. Everything that needs a name rather than an id goes
65//! through the table: `get(b"status")` resolves the name to an id once and then
66//! searches the document by id, which is a binary search over integers.
67
68use core::ops::Bound;
69
70use yo_common::{Code, Error, Result};
71use yo_kv::{Cursor, Elements, Full};
72
73use crate::head::{DEPTH_MAX, Kind};
74use crate::index::{self, IndexKind, Key, PathIndex};
75use crate::path::{Step, Steps};
76use crate::{Builder, Keys, Value};
77
78/// Documents by id, with the key table their keys are interned against.
79#[derive(Debug)]
80pub struct Docs {
81    /// Document id to the document's bytes, the bytes behind the id.
82    rows: Elements<()>,
83    /// The names every interned object in this collection uses.
84    keys: Keys,
85    /// The buffer a write is re-encoded into, kept so a write does not allocate.
86    build: Builder,
87    /// One per indexed path, in the order they were declared.
88    indexes: Vec<PathIndex>,
89    /// The key each index takes from the document being written, one slot per
90    /// index and empty where the document has nothing to file.
91    ///
92    /// Worked out before anything is stored, so a value that cannot be indexed
93    /// fails the write rather than leaving a document behind that no query will
94    /// ever find. Kept on the collection so a write allocates nothing.
95    taken: Vec<Vec<u8>>,
96}
97
98impl Default for Docs {
99    /// Not derived, because the primary table has to be the kind that keeps a
100    /// document behind its id and an empty [`Elements`] is not.
101    fn default() -> Docs {
102        Docs::new()
103    }
104}
105
106impl Docs {
107    /// An empty collection that has not allocated anything yet.
108    #[must_use]
109    pub fn new() -> Docs {
110        Docs {
111            rows: Elements::tailed(0, 0),
112            keys: Keys::new(),
113            build: Builder::new(),
114            indexes: Vec::new(),
115            taken: Vec::new(),
116        }
117    }
118
119    /// An empty collection with room for `n` documents of about `each` bytes.
120    ///
121    /// The ids and the documents share one blob, so the size asked for is the
122    /// two of them together. Getting it wrong costs a growth, not a rewrite.
123    #[must_use]
124    pub fn with_capacity(n: usize, each: usize) -> Docs {
125        Docs {
126            rows: Elements::tailed(n, n.saturating_mul(each)),
127            keys: Keys::new(),
128            build: Builder::with_capacity(each),
129            indexes: Vec::new(),
130            taken: Vec::new(),
131        }
132    }
133
134    /// Store `value` under `id`, and say whether the id is new.
135    ///
136    /// The value is re-encoded with this collection's interned keys on the way
137    /// in. It may not already be interned: a document whose keys are ids
138    /// belongs to whichever collection handed those ids out, and moving it to
139    /// another one without the names is how a collection ends up reading the
140    /// wrong field.
141    pub fn put(&mut self, id: &[u8], value: Value<'_>) -> Result<bool> {
142        self.write(id, value, None)
143    }
144
145    /// Store the document `doc` encodes under `id`, and say whether the id is
146    /// new.
147    ///
148    /// The bytes are checked far enough to be readable and no further, the same
149    /// as [`Value::new`]. A caller holding bytes it did not write should run
150    /// [`Value::validate`] first.
151    pub fn put_bytes(&mut self, id: &[u8], doc: &[u8]) -> Result<bool> {
152        let value = Value::new(doc)
153            .ok_or_else(|| Error::new(Code::Corrupt, "the document is not a readable value"))?;
154        self.write(id, value, Some(doc))
155    }
156
157    /// The write both forms of put go through.
158    ///
159    /// `raw` is the caller's bytes when it had some, so that the path where the
160    /// key table is full stores them directly instead of copying them through
161    /// the builder to get back what it was already holding.
162    ///
163    /// The order is: work out the index keys, un-index whatever was under this
164    /// id, store, index. Working the keys out first is what makes a write that
165    /// cannot be indexed leave the collection exactly as it was, rather than
166    /// storing a document that no query will find.
167    fn write(&mut self, id: &[u8], value: Value<'_>, raw: Option<&[u8]>) -> Result<bool> {
168        let Docs {
169            rows,
170            keys,
171            build,
172            indexes,
173            taken,
174        } = self;
175
176        taken.resize(indexes.len(), Vec::new());
177        for (slot, index) in taken.iter_mut().zip(indexes.iter()) {
178            slot.clear();
179            // The incoming value has its keys as bytes, since put refuses one
180            // that does not, so its paths resolve without the key table.
181            let Some(at) = value.path_bytes(index.path())? else {
182                continue;
183            };
184            if index.keys_at(at, slot).is_err() {
185                return Err(Error::fmt(
186                    Code::Full,
187                    format_args!(
188                        "a value at {} is longer than {} bytes and cannot be indexed",
189                        String::from_utf8_lossy(index.path()),
190                        index::KEY_MAX
191                    ),
192                ));
193            }
194        }
195
196        unindex(rows, keys, indexes, id);
197
198        build.clear();
199        let fresh = if intern_into(keys, build, value, 0)? {
200            store(rows, id, build.finish()?)?
201        } else if let Some(raw) = raw {
202            // The key table filled part way through, and the caller is holding
203            // exactly what should be stored.
204            store(rows, id, raw)?
205        } else {
206            build.clear();
207            build.embed(&value)?;
208            store(rows, id, build.finish()?)?
209        };
210
211        for (slot, index) in taken.iter().zip(indexes.iter_mut()) {
212            let mut filed = Ok(());
213            index::each_key(slot, |key| {
214                if filed.is_ok() {
215                    filed = index.add(key, id);
216                }
217            });
218            filed?;
219        }
220        Ok(fresh)
221    }
222
223    /// Start indexing `path` for equality, and file every document already here
224    /// under it.
225    ///
226    /// Declaring the same path twice is not an error and does not rebuild
227    /// anything, because a caller that opens a collection and declares its
228    /// indexes on the way in should be able to do that every time it opens it.
229    /// An ordered index that is already there stays ordered, since it answers
230    /// equality as well.
231    ///
232    /// The backfill is a path lookup per document, so it costs the collection
233    /// once. There is no background indexer and no window in which the index is
234    /// declared and not yet true, which is Y3.
235    pub fn create_index(&mut self, path: &str) -> Result<()> {
236        self.create_index_bytes(path.as_bytes(), IndexKind::Equality)
237    }
238
239    /// Start indexing `path` for equality and for ranges.
240    ///
241    /// An ordered index is an equality index with a counted B+ tree over the
242    /// rows of its key table, which is the same tree a sorted set ranks with.
243    /// It costs about three bytes per distinct value on top of the equality
244    /// index and a logarithmic search per new value, and it is what
245    /// [`Docs::range`] needs.
246    ///
247    /// A path that is already indexed for equality is upgraded and rebuilt. The
248    /// alternative is answering `Ok` and then having every range on it come back
249    /// empty, which is a query that lies.
250    pub fn create_ordered_index(&mut self, path: &str) -> Result<()> {
251        self.create_index_bytes(path.as_bytes(), IndexKind::Ordered)
252    }
253
254    /// Start indexing every element of the array at `path`.
255    ///
256    /// A document with `["red", "blue"]` there is filed under both, so a search
257    /// for either finds it. A scalar at the path is an array of one, so a
258    /// collection where some documents have a list of tags and some have a
259    /// single tag works without the caller having to normalise it first.
260    ///
261    /// The lookup is [`Docs::find`] with the element as the key, unchanged. An
262    /// array index costs what the document has at the path, so a document with
263    /// ten elements costs ten postings and a document with none costs nothing.
264    pub fn create_array_index(&mut self, path: &str) -> Result<()> {
265        self.create_index_bytes(path.as_bytes(), IndexKind::Array)
266    }
267
268    /// Start indexing every word of the string at `path`.
269    ///
270    /// A document with `"A red bicycle"` there is filed under `a`, `red` and
271    /// `bicycle`, and the lookup is [`Docs::find`] with [`Key::word`] as the
272    /// key. Case is folded on both sides, so a search does not have to know how
273    /// the document was written.
274    ///
275    /// This is a word index and not a search engine. There is no ranking, no
276    /// stemming and no phrase matching, and a path that holds something other
277    /// than a string files nothing. What it answers is which documents contain
278    /// a word, which is a filter, and the ranking that belongs on top of it is
279    /// `10`.
280    pub fn create_text_index(&mut self, path: &str) -> Result<()> {
281        self.create_index_bytes(path.as_bytes(), IndexKind::Text)
282    }
283
284    /// [`Docs::create_index`] and [`Docs::create_ordered_index`] for a path that
285    /// is already bytes.
286    pub fn create_index_bytes(&mut self, path: &[u8], kind: IndexKind) -> Result<()> {
287        for step in Steps::new(path) {
288            step?;
289        }
290        match self.indexes.iter().position(|i| i.path() == path) {
291            // The same kind again is nothing at all, and equality on top of
292            // ordered is already answered. Every other pair means the path is
293            // being asked a different question, so it gets rebuilt.
294            Some(at) if self.indexes[at].kind() == kind => return Ok(()),
295            Some(at)
296                if kind == IndexKind::Equality && self.indexes[at].kind() == IndexKind::Ordered =>
297            {
298                return Ok(());
299            }
300            Some(at) => {
301                self.indexes.remove(at);
302                self.taken.truncate(self.indexes.len());
303            }
304            None => {}
305        }
306        let mut index = PathIndex::new(path, kind);
307        let mut list = Vec::new();
308        for (id, bytes) in self.rows.pairs() {
309            let Some(value) = Value::new(bytes) else {
310                continue;
311            };
312            let doc = Doc {
313                value,
314                keys: &self.keys,
315            };
316            let Some(at) = doc.path_bytes(path)? else {
317                continue;
318            };
319            list.clear();
320            if index.keys_at(at.value(), &mut list).is_err() {
321                return Err(Error::fmt(
322                    Code::Full,
323                    format_args!(
324                        "a value at {} in {} is longer than {} bytes and cannot be indexed",
325                        String::from_utf8_lossy(path),
326                        String::from_utf8_lossy(id),
327                        index::KEY_MAX
328                    ),
329                ));
330            }
331            let mut filed = Ok(());
332            index::each_key(&list, |key| {
333                if filed.is_ok() {
334                    filed = index.add(key, id);
335                }
336            });
337            filed?;
338        }
339        self.indexes.push(index);
340        self.taken.push(Vec::new());
341        Ok(())
342    }
343
344    /// Stop indexing `path`, and say whether it was indexed.
345    pub fn drop_index(&mut self, path: &str) -> bool {
346        self.drop_index_bytes(path.as_bytes())
347    }
348
349    /// [`Docs::drop_index`] for a path that is already bytes.
350    pub fn drop_index_bytes(&mut self, path: &[u8]) -> bool {
351        let Some(at) = self.indexes.iter().position(|i| i.path() == path) else {
352            return false;
353        };
354        self.indexes.remove(at);
355        self.taken.truncate(self.indexes.len());
356        true
357    }
358
359    /// The indexes this collection keeps, in the order they were declared.
360    #[must_use]
361    pub fn indexes(&self) -> &[PathIndex] {
362        &self.indexes
363    }
364
365    /// The index on `path`, if there is one.
366    #[must_use]
367    pub fn index(&self, path: &str) -> Option<&PathIndex> {
368        self.indexes.iter().find(|i| i.path() == path.as_bytes())
369    }
370
371    /// Hand every document whose value at `path` is `key` to `f`, and say how
372    /// many there were.
373    ///
374    /// One probe of the index and one probe of the primary table per document,
375    /// which is the cost model `09` section 5 states rather than hides. A path
376    /// with no index on it is an error and not a scan: a query that silently
377    /// turns into a walk of the collection is the thing this API exists not to
378    /// do.
379    pub fn find(&self, path: &str, key: &Key, mut f: impl FnMut(&[u8], Doc<'_>)) -> Result<usize> {
380        let index = self.index(path).ok_or_else(|| {
381            Error::fmt(
382                Code::Invalid,
383                format_args!("there is no index on {path}, so this would be a scan"),
384            )
385        })?;
386        let Some(set) = index.get(key) else {
387            return Ok(0);
388        };
389        let mut n = 0usize;
390        index::each_id(set, |id| {
391            if let Some(doc) = self.get(id) {
392                f(id, doc);
393                n += 1;
394            }
395        });
396        Ok(n)
397    }
398
399    /// How many documents have `key` at `path`, without reading any of them.
400    ///
401    /// The number a caller sorts its filters by before it intersects them, and
402    /// it is a probe rather than a walk.
403    pub fn count(&self, path: &str, key: &Key) -> Result<usize> {
404        let index = self.index(path).ok_or_else(|| {
405            Error::fmt(
406                Code::Invalid,
407                format_args!("there is no index on {path}, so this would be a scan"),
408            )
409        })?;
410        Ok(index.count(key))
411    }
412
413    /// Hand every document whose value at `path` falls between `lo` and `hi` to
414    /// `f`, smallest first, and say how many there were.
415    ///
416    /// One search of the tree and then a walk, so the cost is the size of the
417    /// answer and not the size of the collection. The bounds are the ordinary
418    /// [`Bound`], so a half open range, a range open at one end and a range open
419    /// at both are all the same call.
420    ///
421    /// The path has to carry an ordered index. An equality index has no order to
422    /// walk, and answering nothing would be a query that lies rather than a
423    /// query that says no.
424    pub fn range(
425        &self,
426        path: &str,
427        lo: Bound<&Key>,
428        hi: Bound<&Key>,
429        mut f: impl FnMut(&[u8], Doc<'_>),
430    ) -> Result<usize> {
431        let index = self.ordered(path)?;
432        let mut n = 0usize;
433        for (_, set) in index.range(lo, hi) {
434            index::each_id(set, |id| {
435                if let Some(doc) = self.get(id) {
436                    f(id, doc);
437                    n += 1;
438                }
439            });
440        }
441        Ok(n)
442    }
443
444    /// [`Docs::range`] backwards, largest value first.
445    pub fn range_rev(
446        &self,
447        path: &str,
448        lo: Bound<&Key>,
449        hi: Bound<&Key>,
450        mut f: impl FnMut(&[u8], Doc<'_>),
451    ) -> Result<usize> {
452        let index = self.ordered(path)?;
453        let mut n = 0usize;
454        for (_, set) in index.range_rev(lo, hi) {
455            index::each_id(set, |id| {
456                if let Some(doc) = self.get(id) {
457                    f(id, doc);
458                    n += 1;
459                }
460            });
461        }
462        Ok(n)
463    }
464
465    /// How many documents fall between `lo` and `hi` at `path`, without reading
466    /// any of them.
467    ///
468    /// This reads the distinct values in the range rather than the documents, so
469    /// a range covering a million documents under a hundred values costs a
470    /// hundred.
471    pub fn count_range(&self, path: &str, lo: Bound<&Key>, hi: Bound<&Key>) -> Result<usize> {
472        Ok(self.ordered(path)?.count_in(lo, hi))
473    }
474
475    /// The ordered index on `path`, or the error that says why there is not one.
476    fn ordered(&self, path: &str) -> Result<&PathIndex> {
477        match self.index(path) {
478            Some(index) if index.kind() == IndexKind::Ordered => Ok(index),
479            Some(_) => Err(Error::fmt(
480                Code::Invalid,
481                format_args!("the index on {path} answers equality and not ranges"),
482            )),
483            None => Err(Error::fmt(
484                Code::Invalid,
485                format_args!("there is no index on {path}, so this would be a scan"),
486            )),
487        }
488    }
489
490    /// The document stored under `id`.
491    #[must_use]
492    pub fn get(&self, id: &[u8]) -> Option<Doc<'_>> {
493        let value = Value::new(self.rows.tail(id)?)?;
494        Some(Doc {
495            value,
496            keys: &self.keys,
497        })
498    }
499
500    /// The stored bytes of the document under `id`, as they sit in the blob.
501    ///
502    /// For a caller that is going to write them somewhere else rather than read
503    /// them, which is `DUMP`, replication and the record plane.
504    #[must_use]
505    pub fn bytes(&self, id: &[u8]) -> Option<&[u8]> {
506        self.rows.tail(id)
507    }
508
509    /// Whether there is a document under `id`.
510    #[must_use]
511    pub fn contains(&self, id: &[u8]) -> bool {
512        self.rows.contains(id)
513    }
514
515    /// Take the document under `id` out, and say whether there was one.
516    ///
517    /// Every index the document was filed in loses it first, so a removal costs
518    /// a path lookup per index on the way out.
519    ///
520    /// The key table is left alone. A name it interned stays interned even if
521    /// this was the last document using it, which is [`Keys`]'s rule and the
522    /// reason an id is a row index.
523    pub fn remove(&mut self, id: &[u8]) -> bool {
524        let Docs {
525            rows,
526            keys,
527            indexes,
528            ..
529        } = self;
530        unindex(rows, keys, indexes, id);
531        rows.remove(id).is_some()
532    }
533
534    /// How many documents there are.
535    #[must_use]
536    pub fn len(&self) -> usize {
537        self.rows.len()
538    }
539
540    /// Whether the collection holds nothing.
541    #[must_use]
542    pub fn is_empty(&self) -> bool {
543        self.rows.is_empty()
544    }
545
546    /// The names this collection has interned.
547    #[must_use]
548    pub fn keys(&self) -> &Keys {
549        &self.keys
550    }
551
552    /// Every document, in insertion order.
553    pub fn iter(&self) -> impl Iterator<Item = (&[u8], Doc<'_>)> {
554        let keys = &self.keys;
555        self.rows.pairs().filter_map(move |(id, bytes)| {
556            let value = Value::new(bytes)?;
557            Some((id, Doc { value, keys }))
558        })
559    }
560
561    /// Walk part of the collection and say where to resume, the same contract
562    /// [`Elements::scan`] has.
563    pub fn scan<F>(&self, cursor: Cursor, count: usize, mut f: F) -> Cursor
564    where
565        F: FnMut(&[u8], Doc<'_>),
566    {
567        let keys = &self.keys;
568        self.rows.scan_pairs(cursor, count, |id, bytes| {
569            if let Some(value) = Value::new(bytes) {
570                f(id, Doc { value, keys });
571            }
572        })
573    }
574
575    /// Throw every document away and keep the key table and the allocations.
576    ///
577    /// The indexes stay declared and go empty, for the same reason the key table
578    /// stays: a caller that empties a collection is refilling it, and an index
579    /// that quietly disappeared when the last document did would turn the next
580    /// query into an error.
581    ///
582    /// The key table stays because a collection that is emptied is usually a
583    /// collection that is about to be refilled with the same shape of document,
584    /// and relearning twenty names is work with nothing to show for it.
585    pub fn clear(&mut self) {
586        self.rows.clear();
587        self.build.clear();
588        for index in &mut self.indexes {
589            index.clear();
590        }
591    }
592
593    /// What the collection costs, the key table and the indexes included.
594    #[must_use]
595    pub fn memory_bytes(&self) -> usize {
596        self.rows.memory_bytes()
597            + self.keys.memory_bytes()
598            + self
599                .indexes
600                .iter()
601                .map(PathIndex::memory_bytes)
602                .sum::<usize>()
603    }
604}
605
606/// Take whatever is stored under `id` out of every index, leaving the primary
607/// table alone.
608///
609/// Both an overwrite and a removal go through here, because both of them make
610/// the old document's index entries wrong and neither of them can work out what
611/// those entries were once the bytes are gone. Nothing here can fail: a document
612/// that is no longer readable, or a path that no longer resolves, simply has
613/// nothing filed under it, and refusing a removal because the thing being
614/// removed is damaged is the wrong answer.
615fn unindex(rows: &Elements<()>, keys: &Keys, indexes: &mut [PathIndex], id: &[u8]) {
616    if indexes.is_empty() {
617        return;
618    }
619    let Some(bytes) = rows.tail(id) else {
620        return;
621    };
622    let Some(value) = Value::new(bytes) else {
623        return;
624    };
625    let doc = Doc { value, keys };
626    let mut list = Vec::new();
627    for index in indexes {
628        let Ok(Some(at)) = doc.path_bytes(index.path()) else {
629            continue;
630        };
631        list.clear();
632        // The keys came out of this same code on the way in, so a key that was
633        // refused then is not filed now and there is nothing to take out.
634        let _ = index.keys_at(at.value(), &mut list);
635        index::each_key(&list, |key| index.take(key, id));
636    }
637}
638
639/// Put `bytes` in the primary table under `id`, turning a refusal into the
640/// error the layer above would have written anyway.
641fn store(rows: &mut Elements<()>, id: &[u8], bytes: &[u8]) -> Result<bool> {
642    match rows.set_tailed(id, bytes, ()) {
643        Ok((_, fresh)) => Ok(fresh),
644        Err(Full::Name) => Err(Error::fmt(
645            Code::Full,
646            format_args!("a document id is at most {} bytes", yo_kv::NAME_MAX),
647        )),
648        Err(Full::Rows) => Err(Error::fmt(
649            Code::Full,
650            format_args!("a collection holds at most {} documents", yo_kv::MAX_ROWS),
651        )),
652    }
653}
654
655/// Write `value` into `b` with every object key replaced by its id.
656///
657/// `Ok(false)` means the key table ran out of ids part way through, and the
658/// caller stores the document with its keys as bytes instead. The builder is
659/// left half open in that case, so the caller clears it.
660///
661/// The recursion is bounded by the builder: it refuses to open a container more
662/// than [`DEPTH_MAX`] deep, so a document nested deeper than that, which only a
663/// damaged one can be, stops with an error rather than with the stack.
664fn intern_into(keys: &mut Keys, b: &mut Builder, value: Value<'_>, depth: usize) -> Result<bool> {
665    let corrupt = || Error::new(Code::Corrupt, "the document is not readable at that point");
666    match value.kind() {
667        Kind::Null => b.null()?,
668        Kind::Bool => b.bool(value.as_bool().ok_or_else(corrupt)?)?,
669        Kind::Int => b.int(value.as_int().ok_or_else(corrupt)?)?,
670        Kind::Float => b.float(value.as_float().ok_or_else(corrupt)?)?,
671        Kind::Text => b.text_bytes(value.text_bytes().ok_or_else(corrupt)?)?,
672        Kind::Array => {
673            b.begin_array()?;
674            for i in 0..value.len() {
675                let child = value.at(i).ok_or_else(corrupt)?;
676                if !intern_into(keys, b, child, depth + 1)? {
677                    return Ok(false);
678                }
679            }
680            b.end_array()?;
681        }
682        Kind::Object => {
683            if value.is_interned() {
684                return Err(Error::new(
685                    Code::Invalid,
686                    "this document's keys are ids from another collection's key table",
687                ));
688            }
689            b.begin_object_interned()?;
690            for i in 0..value.len() {
691                let name = value.key_at(i).ok_or_else(corrupt)?;
692                let Some(id) = keys.intern(name) else {
693                    return Ok(false);
694                };
695                b.key_id(id)?;
696                let child = value.at(i).ok_or_else(corrupt)?;
697                if !intern_into(keys, b, child, depth + 1)? {
698                    return Ok(false);
699                }
700            }
701            b.end_object()?;
702        }
703    }
704    debug_assert!(depth <= DEPTH_MAX, "the builder caps the depth");
705    Ok(true)
706}
707
708/// A value with the key table its keys are interned against.
709///
710/// Everything a [`Value`] offers is here too, and the things that need a name
711/// rather than an id, which are a lookup, a walk over the members and printing
712/// the thing, go through the table. A document whose keys are bytes works the
713/// same way and simply never asks the table anything, so a caller does not have
714/// to know which kind it is holding.
715#[derive(Clone, Copy)]
716pub struct Doc<'a> {
717    value: Value<'a>,
718    keys: &'a Keys,
719}
720
721impl<'a> Doc<'a> {
722    /// A view of `value` against `keys`.
723    #[must_use]
724    pub fn new(value: Value<'a>, keys: &'a Keys) -> Doc<'a> {
725        Doc { value, keys }
726    }
727
728    /// The value underneath, for the accessors that never need a name.
729    #[must_use]
730    pub fn value(&self) -> Value<'a> {
731        self.value
732    }
733
734    /// The key table this reads names out of.
735    #[must_use]
736    pub fn keys(&self) -> &'a Keys {
737        self.keys
738    }
739
740    /// What this value is.
741    #[must_use]
742    pub fn kind(&self) -> Kind {
743        self.value.kind()
744    }
745
746    /// Whether this is `null`.
747    #[must_use]
748    pub fn is_null(&self) -> bool {
749        self.value.is_null()
750    }
751
752    /// The boolean this holds, if it holds one.
753    #[must_use]
754    pub fn as_bool(&self) -> Option<bool> {
755        self.value.as_bool()
756    }
757
758    /// The integer this holds, if it holds one.
759    #[must_use]
760    pub fn as_int(&self) -> Option<i64> {
761        self.value.as_int()
762    }
763
764    /// The float this holds, if it holds one.
765    #[must_use]
766    pub fn as_float(&self) -> Option<f64> {
767        self.value.as_float()
768    }
769
770    /// The string this holds, if it holds one and it is UTF-8.
771    #[must_use]
772    pub fn as_text(&self) -> Option<&'a str> {
773        self.value.as_text()
774    }
775
776    /// The string this holds as it is stored, without the UTF-8 check.
777    #[must_use]
778    pub fn text_bytes(&self) -> Option<&'a [u8]> {
779        self.value.text_bytes()
780    }
781
782    /// How many elements a container holds. Zero for anything else.
783    #[must_use]
784    pub fn len(&self) -> usize {
785        self.value.len()
786    }
787
788    /// Whether this is a container with nothing in it.
789    #[must_use]
790    pub fn is_empty(&self) -> bool {
791        self.value.is_empty()
792    }
793
794    /// The value stored under `key`.
795    ///
796    /// For an interned object this is a name to id lookup in the table and then
797    /// a binary search over integers. A name the table has never seen cannot be
798    /// in the document, so it answers `None` without touching the document at
799    /// all.
800    #[must_use]
801    pub fn get(&self, key: &[u8]) -> Option<Doc<'a>> {
802        let value = if self.value.is_interned() {
803            self.value.get_id(self.keys.id(key)?)?
804        } else {
805            self.value.get(key)?
806        };
807        Some(Doc {
808            value,
809            keys: self.keys,
810        })
811    }
812
813    /// Element `i` of a container, in the container's own order.
814    #[must_use]
815    pub fn at(&self, i: usize) -> Option<Doc<'a>> {
816        Some(Doc {
817            value: self.value.at(i)?,
818            keys: self.keys,
819        })
820    }
821
822    /// The name of member `i` of an object, whichever way the keys are stored.
823    #[must_use]
824    pub fn key_at(&self, i: usize) -> Option<&'a [u8]> {
825        if self.value.is_interned() {
826            self.keys.name(self.value.key_id_at(i)?)
827        } else {
828            self.value.key_at(i)
829        }
830    }
831
832    /// Every member of an object, name first, in the order the document stores
833    /// them.
834    ///
835    /// That order is by key id for an interned object and by key bytes for one
836    /// whose keys are bytes, so it is stable for a given collection and it is
837    /// not alphabetical. Sort it if the order is part of the answer.
838    #[must_use]
839    pub fn members(&self) -> DocMembers<'a> {
840        DocMembers { d: *self, i: 0 }
841    }
842
843    /// Every element of a container, in the container's own order.
844    #[must_use]
845    pub fn iter(&self) -> DocElems<'a> {
846        DocElems { d: *self, i: 0 }
847    }
848
849    /// The value at `path`, where a path names exactly one place.
850    ///
851    /// The same grammar [`Value::path`] takes, with the names resolved through
852    /// the key table on the way down.
853    pub fn path(&self, path: &str) -> Result<Option<Doc<'a>>> {
854        self.path_bytes(path.as_bytes())
855    }
856
857    /// [`Doc::path`] for a path that is already bytes.
858    pub fn path_bytes(&self, path: &[u8]) -> Result<Option<Doc<'a>>> {
859        let mut at = *self;
860        for step in Steps::new(path) {
861            let next = match step? {
862                Step::Key(k) => at.get(k),
863                Step::Index(_) if at.kind() != Kind::Array => None,
864                Step::Index(i) => {
865                    let n = at.len();
866                    let i = if i < 0 {
867                        match n.checked_sub(i.unsigned_abs() as usize) {
868                            Some(i) => i,
869                            None => return Ok(None),
870                        }
871                    } else {
872                        i as usize
873                    };
874                    at.at(i)
875                }
876            };
877            let Some(next) = next else {
878                return Ok(None);
879            };
880            at = next;
881        }
882        Ok(Some(at))
883    }
884}
885
886impl core::fmt::Debug for Doc<'_> {
887    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
888        match self.kind() {
889            Kind::Object => {
890                let mut m = f.debug_map();
891                for (k, v) in self.members() {
892                    m.entry(&String::from_utf8_lossy(k), &v);
893                }
894                m.finish()
895            }
896            Kind::Array => f.debug_list().entries(self.iter()).finish(),
897            _ => self.value.fmt(f),
898        }
899    }
900}
901
902/// Every member of an object, from [`Doc::members`].
903#[derive(Clone)]
904pub struct DocMembers<'a> {
905    d: Doc<'a>,
906    i: usize,
907}
908
909impl<'a> Iterator for DocMembers<'a> {
910    type Item = (&'a [u8], Doc<'a>);
911
912    fn next(&mut self) -> Option<(&'a [u8], Doc<'a>)> {
913        let key = self.d.key_at(self.i)?;
914        let val = self.d.at(self.i)?;
915        self.i += 1;
916        Some((key, val))
917    }
918
919    fn size_hint(&self) -> (usize, Option<usize>) {
920        let left = self.d.len().saturating_sub(self.i);
921        (left, Some(left))
922    }
923}
924
925/// Every element of a container, from [`Doc::iter`].
926#[derive(Clone)]
927pub struct DocElems<'a> {
928    d: Doc<'a>,
929    i: usize,
930}
931
932impl<'a> Iterator for DocElems<'a> {
933    type Item = Doc<'a>;
934
935    fn next(&mut self) -> Option<Doc<'a>> {
936        let out = self.d.at(self.i)?;
937        self.i += 1;
938        Some(out)
939    }
940
941    fn size_hint(&self) -> (usize, Option<usize>) {
942        let left = self.d.len().saturating_sub(self.i);
943        (left, Some(left))
944    }
945}
946
947#[cfg(test)]
948mod tests {
949    use super::*;
950
951    /// An order, the shape `09` section 5 uses as its example.
952    fn order(id: i64, status: &str, lines: usize) -> Vec<u8> {
953        let mut b = Builder::new();
954        b.begin_object().expect("open");
955        b.key(b"id").expect("key");
956        b.int(id).expect("value");
957        b.key(b"customer").expect("key");
958        b.int(id * 7).expect("value");
959        b.key(b"status").expect("key");
960        b.text(status).expect("value");
961        b.key(b"lines").expect("key");
962        b.begin_array().expect("open");
963        for i in 0..lines {
964            b.begin_object().expect("open");
965            b.key(b"sku").expect("key");
966            b.text(&format!("sku-{i}")).expect("value");
967            b.key(b"qty").expect("key");
968            b.int(i as i64 + 1).expect("value");
969            b.end_object().expect("close");
970        }
971        b.end_array().expect("close");
972        b.end_object().expect("close");
973        b.finish().expect("finished").to_vec()
974    }
975
976    #[test]
977    fn a_document_reads_back_the_way_it_went_in() {
978        let mut docs = Docs::new();
979        assert!(
980            docs.put_bytes(b"order:1", &order(1, "open", 3))
981                .expect("put")
982        );
983        assert!(
984            !docs
985                .put_bytes(b"order:1", &order(1, "shut", 3))
986                .expect("put")
987        );
988        assert_eq!(docs.len(), 1);
989
990        let d = docs.get(b"order:1").expect("stored");
991        assert_eq!(d.get(b"id").and_then(|v| v.as_int()), Some(1));
992        assert_eq!(d.get(b"status").and_then(|v| v.as_text()), Some("shut"));
993        assert_eq!(d.get(b"lines").map(|v| v.len()), Some(3));
994        assert_eq!(
995            d.path("$.lines[1].sku")
996                .expect("a path")
997                .and_then(|v| v.as_text()),
998            Some("sku-1")
999        );
1000        assert_eq!(
1001            d.path("$.lines[-1].qty")
1002                .expect("a path")
1003                .and_then(|v| v.as_int()),
1004            Some(3)
1005        );
1006        assert!(d.get(b"missing").is_none());
1007    }
1008
1009    #[test]
1010    fn the_keys_are_interned_and_the_names_come_back() {
1011        let mut docs = Docs::new();
1012        docs.put_bytes(b"order:1", &order(1, "open", 2))
1013            .expect("put");
1014        let names: Vec<String> = docs
1015            .keys()
1016            .iter()
1017            .map(|(n, _)| String::from_utf8_lossy(n).into_owned())
1018            .collect();
1019        names.iter().for_each(|n| assert!(!n.is_empty()));
1020        assert_eq!(
1021            docs.keys().len(),
1022            6,
1023            "id customer status lines sku qty: {names:?}"
1024        );
1025
1026        let d = docs.get(b"order:1").expect("stored");
1027        assert!(d.value().is_interned());
1028        let mut got: Vec<&[u8]> = d.members().map(|(k, _)| k).collect();
1029        got.sort_unstable();
1030        assert_eq!(got, [&b"customer"[..], b"id", b"lines", b"status"]);
1031        let line = d.path("$.lines[0]").expect("a path").expect("there");
1032        assert!(line.value().is_interned());
1033        let mut inner: Vec<&[u8]> = line.members().map(|(k, _)| k).collect();
1034        inner.sort_unstable();
1035        assert_eq!(inner, [&b"qty"[..], b"sku"]);
1036    }
1037
1038    /// Store 256 copies of a shape and say what fraction of the bytes survived.
1039    fn shrinkage(shape: impl Fn(i64) -> Vec<u8>) -> f64 {
1040        let mut docs = Docs::new();
1041        let mut plain = 0usize;
1042        for i in 0..256i64 {
1043            let bytes = shape(i);
1044            plain += bytes.len();
1045            docs.put_bytes(format!("d:{i}").as_bytes(), &bytes)
1046                .expect("put");
1047        }
1048        let stored: usize = (0..256i64)
1049            .map(|i| {
1050                docs.bytes(format!("d:{i}").as_bytes())
1051                    .expect("stored")
1052                    .len()
1053            })
1054            .sum();
1055        stored as f64 / plain as f64
1056    }
1057
1058    #[test]
1059    fn interning_makes_a_collection_of_the_same_shape_smaller() {
1060        // The claim in `09` section 4 is that the same field names on every
1061        // document are most of what a document collection costs, and that
1062        // interning them is worth about forty percent. How much it is actually
1063        // worth depends on how much of a document is names, so both ends are
1064        // measured here rather than one number being asserted twice.
1065        //
1066        // A document that is mostly names, which is what a typed collection of
1067        // small records looks like, keeps a little over half its bytes.
1068        let names = shrinkage(|i| {
1069            let mut b = Builder::new();
1070            b.begin_object().expect("open");
1071            for f in 0..20 {
1072                b.key(format!("some_field_name_{f:02}").as_bytes())
1073                    .expect("key");
1074                b.int(i + f).expect("value");
1075            }
1076            b.end_object().expect("close");
1077            b.finish().expect("finished").to_vec()
1078        });
1079        assert!(names < 0.60, "a document of names kept {names}");
1080
1081        // An order, which carries real payload as well, keeps about three
1082        // quarters. That is the honest floor for the claim and it is still a
1083        // fifth of the collection gone for nothing but a table of twenty
1084        // strings.
1085        let orders = shrinkage(|i| order(i, "open", 2));
1086        assert!(orders < 0.80, "an order collection kept {orders}");
1087    }
1088
1089    #[test]
1090    fn a_document_whose_keys_are_already_ids_is_refused() {
1091        let mut b = Builder::new();
1092        b.begin_object_interned().expect("open");
1093        b.key_id(0).expect("key");
1094        b.int(1).expect("value");
1095        b.end_object().expect("close");
1096        let bytes = b.finish().expect("finished").to_vec();
1097
1098        let mut docs = Docs::new();
1099        let err = docs.put_bytes(b"x", &bytes).expect_err("refused");
1100        assert_eq!(err.code(), Code::Invalid);
1101    }
1102
1103    #[test]
1104    fn a_document_that_is_not_readable_is_refused() {
1105        let mut docs = Docs::new();
1106        let err = docs.put_bytes(b"x", &[2, 0, 0, 0]).expect_err("refused");
1107        assert_eq!(err.code(), Code::Corrupt);
1108        assert!(docs.is_empty());
1109    }
1110
1111    #[test]
1112    fn a_removal_leaves_every_other_document_where_it_was() {
1113        let mut docs = Docs::new();
1114        for i in 0..64i64 {
1115            docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, "open", 1))
1116                .expect("put");
1117        }
1118        for i in (0..64i64).step_by(3) {
1119            assert!(docs.remove(format!("order:{i}").as_bytes()));
1120        }
1121        assert_eq!(docs.len(), 64 - 22);
1122        for i in 0..64i64 {
1123            let id = format!("order:{i}");
1124            match docs.get(id.as_bytes()) {
1125                Some(d) => {
1126                    assert!(i % 3 != 0, "{id} was removed");
1127                    assert_eq!(d.get(b"id").and_then(|v| v.as_int()), Some(i));
1128                }
1129                None => assert!(i % 3 == 0, "{id} was not removed"),
1130            }
1131        }
1132        assert_eq!(docs.keys().len(), 6, "a removal does not un-intern a name");
1133    }
1134
1135    #[test]
1136    fn a_walk_sees_every_document_once() {
1137        let mut docs = Docs::new();
1138        for i in 0..200i64 {
1139            docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, "open", 1))
1140                .expect("put");
1141        }
1142
1143        let mut seen: Vec<i64> = docs
1144            .iter()
1145            .map(|(_, d)| d.get(b"id").and_then(|v| v.as_int()).expect("an id"))
1146            .collect();
1147        seen.sort_unstable();
1148        assert_eq!(seen, (0..200).collect::<Vec<i64>>());
1149
1150        let mut scanned = Vec::new();
1151        let mut cursor = Cursor::START;
1152        loop {
1153            cursor = docs.scan(cursor, 16, |id, _| scanned.push(id.to_vec()));
1154            if cursor.is_end() {
1155                break;
1156            }
1157        }
1158        scanned.sort_unstable();
1159        scanned.dedup();
1160        assert_eq!(scanned.len(), 200);
1161    }
1162
1163    #[test]
1164    fn an_empty_collection_answers_nothing_rather_than_failing() {
1165        let docs = Docs::new();
1166        assert!(docs.is_empty());
1167        assert!(docs.get(b"nothing").is_none());
1168        assert!(docs.bytes(b"nothing").is_none());
1169        assert!(!docs.contains(b"nothing"));
1170        assert_eq!(docs.iter().count(), 0);
1171    }
1172
1173    #[test]
1174    fn a_document_prints_with_its_names_back_on() {
1175        let mut docs = Docs::new();
1176        docs.put_bytes(b"order:1", &order(1, "open", 1))
1177            .expect("put");
1178        let text = format!("{:?}", docs.get(b"order:1").expect("stored"));
1179        assert!(text.contains("\"status\": \"open\""), "{text}");
1180        assert!(text.contains("\"sku\": \"sku-0\""), "{text}");
1181    }
1182
1183    /// The ids `find` answers for one key, sorted so a test can compare them.
1184    fn found(docs: &Docs, path: &str, key: &Key) -> Vec<String> {
1185        let mut out = Vec::new();
1186        let n = docs
1187            .find(path, key, |id, d| {
1188                assert!(!d.is_empty(), "the document came back whole");
1189                out.push(String::from_utf8_lossy(id).into_owned());
1190            })
1191            .expect("indexed");
1192        assert_eq!(n, out.len(), "the count is what the callback saw");
1193        out.sort();
1194        out
1195    }
1196
1197    #[test]
1198    fn an_index_declared_after_the_documents_finds_them() {
1199        let mut docs = Docs::new();
1200        for i in 0..64i64 {
1201            let status = if i % 4 == 0 { "shut" } else { "open" };
1202            docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, status, 1))
1203                .expect("put");
1204        }
1205        docs.create_index("$.status").expect("indexed");
1206        assert_eq!(docs.index("$.status").expect("there").len(), 2);
1207        assert_eq!(docs.count("$.status", &Key::text("shut")).expect("i"), 16);
1208        assert_eq!(docs.count("$.status", &Key::text("open")).expect("i"), 48);
1209        assert_eq!(found(&docs, "$.status", &Key::text("shut")).len(), 16);
1210        assert!(found(&docs, "$.status", &Key::text("gone")).is_empty());
1211
1212        // A document written after the index exists is filed by the write.
1213        docs.put_bytes(b"order:64", &order(64, "shut", 1))
1214            .expect("put");
1215        assert_eq!(docs.count("$.status", &Key::text("shut")).expect("i"), 17);
1216    }
1217
1218    #[test]
1219    fn an_overwrite_moves_a_document_from_one_key_to_the_other() {
1220        let mut docs = Docs::new();
1221        docs.create_index("$.status").expect("indexed");
1222        docs.put_bytes(b"order:1", &order(1, "open", 1))
1223            .expect("put");
1224        assert_eq!(found(&docs, "$.status", &Key::text("open")), ["order:1"]);
1225
1226        docs.put_bytes(b"order:1", &order(1, "shut", 1))
1227            .expect("put");
1228        assert!(
1229            found(&docs, "$.status", &Key::text("open")).is_empty(),
1230            "the old key kept it"
1231        );
1232        assert_eq!(found(&docs, "$.status", &Key::text("shut")), ["order:1"]);
1233        assert_eq!(docs.index("$.status").expect("there").postings(), 1);
1234    }
1235
1236    #[test]
1237    fn a_removal_takes_a_document_out_of_every_index() {
1238        let mut docs = Docs::new();
1239        docs.create_index("$.status").expect("indexed");
1240        docs.create_index("$.customer").expect("indexed");
1241        for i in 0..8i64 {
1242            docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, "open", 1))
1243                .expect("put");
1244        }
1245        assert!(docs.remove(b"order:3"));
1246        assert_eq!(found(&docs, "$.status", &Key::text("open")).len(), 7);
1247        assert_eq!(docs.count("$.customer", &Key::int(21)).expect("i"), 0);
1248        assert_eq!(docs.count("$.customer", &Key::int(28)).expect("i"), 1);
1249        for index in docs.indexes() {
1250            assert_eq!(index.postings(), 7);
1251        }
1252
1253        assert!(!docs.remove(b"order:3"), "it is already gone");
1254        assert_eq!(docs.index("$.status").expect("there").postings(), 7);
1255    }
1256
1257    #[test]
1258    fn a_path_that_names_a_container_or_nothing_is_simply_not_filed() {
1259        let mut docs = Docs::new();
1260        docs.create_index("$.lines").expect("indexed");
1261        docs.create_index("$.shipped").expect("indexed");
1262        docs.create_index("$.lines[0].qty").expect("indexed");
1263        for i in 0..4i64 {
1264            docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, "open", 2))
1265                .expect("put");
1266        }
1267        assert_eq!(docs.len(), 4);
1268        assert!(
1269            docs.index("$.lines").expect("there").is_empty(),
1270            "an array has no equality key"
1271        );
1272        assert!(
1273            docs.index("$.shipped").expect("there").is_empty(),
1274            "no document has that path"
1275        );
1276        assert_eq!(
1277            docs.count("$.lines[0].qty", &Key::int(1)).expect("i"),
1278            4,
1279            "a path through an array reaches a scalar"
1280        );
1281    }
1282
1283    #[test]
1284    fn a_value_too_long_to_index_fails_the_write_and_stores_nothing() {
1285        let mut b = Builder::new();
1286        b.begin_object().expect("open");
1287        b.key(b"status").expect("key");
1288        b.text(&"x".repeat(crate::KEY_MAX)).expect("value");
1289        b.end_object().expect("close");
1290        let huge = b.finish().expect("finished").to_vec();
1291
1292        let mut docs = Docs::new();
1293        docs.create_index("$.status").expect("indexed");
1294        let err = docs.put_bytes(b"order:1", &huge).expect_err("refused");
1295        assert_eq!(err.code(), Code::Full);
1296        assert!(
1297            docs.is_empty(),
1298            "a write that cannot be indexed leaves nothing behind"
1299        );
1300
1301        // Without the index it is an ordinary document and goes in fine.
1302        assert!(docs.drop_index("$.status"));
1303        docs.put_bytes(b"order:1", &huge).expect("put");
1304        assert_eq!(docs.len(), 1);
1305    }
1306
1307    #[test]
1308    fn a_query_on_a_path_with_no_index_says_so_rather_than_scanning() {
1309        let mut docs = Docs::new();
1310        docs.put_bytes(b"order:1", &order(1, "open", 1))
1311            .expect("put");
1312        let err = docs
1313            .find("$.status", &Key::text("open"), |_, _| ())
1314            .expect_err("refused");
1315        assert_eq!(err.code(), Code::Invalid);
1316        assert_eq!(
1317            docs.count("$.status", &Key::text("open"))
1318                .expect_err("refused")
1319                .code(),
1320            Code::Invalid
1321        );
1322        assert!(docs.index("$.status").is_none());
1323        assert!(!docs.drop_index("$.status"));
1324    }
1325
1326    #[test]
1327    fn declaring_the_same_index_twice_leaves_the_first_one_alone() {
1328        let mut docs = Docs::new();
1329        docs.create_index("$.status").expect("indexed");
1330        docs.put_bytes(b"order:1", &order(1, "open", 1))
1331            .expect("put");
1332        docs.create_index("$.status").expect("indexed again");
1333        assert_eq!(docs.indexes().len(), 1);
1334        assert_eq!(
1335            docs.index("$.status").expect("there").postings(),
1336            1,
1337            "a redeclaration did not double file anything"
1338        );
1339        assert!(docs.create_index("$.[").is_err(), "the path has to parse");
1340    }
1341
1342    #[test]
1343    fn clearing_a_collection_empties_its_indexes_and_keeps_them() {
1344        let mut docs = Docs::new();
1345        docs.create_index("$.status").expect("indexed");
1346        for i in 0..8i64 {
1347            docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, "open", 1))
1348                .expect("put");
1349        }
1350        docs.clear();
1351        assert!(docs.is_empty());
1352        assert!(docs.index("$.status").expect("still declared").is_empty());
1353        assert_eq!(docs.count("$.status", &Key::text("open")).expect("i"), 0);
1354
1355        docs.put_bytes(b"order:9", &order(9, "open", 1))
1356            .expect("put");
1357        assert_eq!(found(&docs, "$.status", &Key::text("open")), ["order:9"]);
1358    }
1359
1360    #[test]
1361    fn two_indexes_intersect_as_the_sets_they_are() {
1362        let mut docs = Docs::new();
1363        docs.create_index("$.status").expect("indexed");
1364        docs.create_index("$.customer").expect("indexed");
1365        for i in 0..32i64 {
1366            let status = if i % 2 == 0 { "open" } else { "shut" };
1367            docs.put_bytes(format!("order:{i}").as_bytes(), &order(i % 4, status, 1))
1368                .expect("put");
1369        }
1370
1371        // What a planner does: probe both, walk the smaller, ask the larger.
1372        // That is `SINTER` and there is no code here that is not already the
1373        // set's.
1374        let open = Key::text("open");
1375        let customer = Key::int(14);
1376        let small = docs.count("$.customer", &customer).expect("indexed");
1377        let large = docs.count("$.status", &open).expect("indexed");
1378        assert_eq!((small, large), (8, 16));
1379
1380        let small = docs.index("$.customer").expect("there").get(&customer);
1381        let large = docs.index("$.status").expect("there").get(&open);
1382        let (Some(small), Some(large)) = (small, large) else {
1383            panic!("both keys are filed");
1384        };
1385        let mut both = Vec::new();
1386        index::each_id(small, |id| {
1387            if large.contains(id) {
1388                both.push(String::from_utf8_lossy(id).into_owned());
1389            }
1390        });
1391        both.sort();
1392        assert_eq!(
1393            both,
1394            [
1395                "order:10", "order:14", "order:18", "order:2", "order:22", "order:26", "order:30",
1396                "order:6"
1397            ]
1398        );
1399    }
1400
1401    /// The customer numbers a range answers, in the order it answered them.
1402    fn ranged(docs: &Docs, lo: Bound<&Key>, hi: Bound<&Key>) -> Vec<i64> {
1403        let mut out = Vec::new();
1404        let n = docs
1405            .range("$.customer", lo, hi, |_, d| {
1406                out.push(d.get(b"customer").and_then(|v| v.as_int()).expect("there"));
1407            })
1408            .expect("ordered");
1409        assert_eq!(n, out.len());
1410
1411        let mut back = Vec::new();
1412        docs.range_rev("$.customer", lo, hi, |_, d| {
1413            back.push(d.get(b"customer").and_then(|v| v.as_int()).expect("there"));
1414        })
1415        .expect("ordered");
1416        back.reverse();
1417        assert_eq!(out, back, "backwards is forwards read the other way");
1418        assert_eq!(
1419            docs.count_range("$.customer", lo, hi).expect("ordered"),
1420            out.len()
1421        );
1422        out
1423    }
1424
1425    #[test]
1426    fn an_ordered_index_answers_a_range_in_order() {
1427        let mut docs = Docs::new();
1428        docs.create_ordered_index("$.customer").expect("ordered");
1429        // Customer is seven times the id, so the values are 0, 7, 14 and on.
1430        for i in 0..64i64 {
1431            docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, "open", 1))
1432                .expect("put");
1433        }
1434
1435        assert_eq!(
1436            ranged(&docs, Bound::Unbounded, Bound::Unbounded),
1437            (0..64i64).map(|i| i * 7).collect::<Vec<i64>>()
1438        );
1439        let (lo, hi) = (Key::int(70), Key::int(105));
1440        assert_eq!(
1441            ranged(&docs, Bound::Included(&lo), Bound::Included(&hi)),
1442            [70, 77, 84, 91, 98, 105]
1443        );
1444        assert_eq!(
1445            ranged(&docs, Bound::Excluded(&lo), Bound::Excluded(&hi)),
1446            [77, 84, 91, 98]
1447        );
1448        // Bounds that fall between two values, which is the ordinary case.
1449        assert_eq!(
1450            ranged(
1451                &docs,
1452                Bound::Included(&Key::int(71)),
1453                Bound::Excluded(&Key::int(90))
1454            ),
1455            [77, 84]
1456        );
1457        assert!(ranged(&docs, Bound::Included(&Key::int(442)), Bound::Unbounded).is_empty());
1458
1459        // Equality still works on the same index.
1460        assert_eq!(docs.count("$.customer", &Key::int(70)).expect("i"), 1);
1461        assert_eq!(
1462            docs.index("$.customer").expect("there").kind(),
1463            IndexKind::Ordered
1464        );
1465    }
1466
1467    #[test]
1468    fn a_range_stays_right_through_writes_and_removals() {
1469        let mut docs = Docs::new();
1470        for i in 0..128i64 {
1471            docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, "open", 1))
1472                .expect("put");
1473        }
1474        // Declared after the fact, so this is the backfill and not the write
1475        // path putting the tree together.
1476        docs.create_ordered_index("$.customer").expect("ordered");
1477        assert_eq!(ranged(&docs, Bound::Unbounded, Bound::Unbounded).len(), 128);
1478
1479        // Every removal moves the key table's last row into the hole, so this is
1480        // the renumbering going through the whole collection.
1481        for i in (0..128i64).step_by(2) {
1482            assert!(docs.remove(format!("order:{i}").as_bytes()));
1483        }
1484        assert_eq!(
1485            ranged(&docs, Bound::Unbounded, Bound::Unbounded),
1486            (0..128i64)
1487                .filter(|i| i % 2 == 1)
1488                .map(|i| i * 7)
1489                .collect::<Vec<i64>>()
1490        );
1491
1492        // And an overwrite that moves a document from one key to another.
1493        docs.put_bytes(b"order:1", &order(200, "open", 1))
1494            .expect("put");
1495        let after = ranged(&docs, Bound::Unbounded, Bound::Unbounded);
1496        assert_eq!(after.first(), Some(&21), "seven is gone");
1497        assert_eq!(after.last(), Some(&1400), "and it came back at the top");
1498    }
1499
1500    #[test]
1501    fn an_equality_index_refuses_a_range_rather_than_answering_nothing() {
1502        let mut docs = Docs::new();
1503        docs.create_index("$.customer").expect("indexed");
1504        docs.put_bytes(b"order:1", &order(1, "open", 1))
1505            .expect("put");
1506        let err = docs
1507            .range("$.customer", Bound::Unbounded, Bound::Unbounded, |_, _| ())
1508            .expect_err("refused");
1509        assert_eq!(err.code(), Code::Invalid);
1510        assert!(err.to_string().contains("equality"), "{err}");
1511        assert_eq!(
1512            docs.range("$.status", Bound::Unbounded, Bound::Unbounded, |_, _| ())
1513                .expect_err("refused")
1514                .code(),
1515            Code::Invalid
1516        );
1517    }
1518
1519    #[test]
1520    fn asking_for_an_order_on_an_equality_index_upgrades_it() {
1521        let mut docs = Docs::new();
1522        docs.create_index("$.customer").expect("indexed");
1523        for i in 0..8i64 {
1524            docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, "open", 1))
1525                .expect("put");
1526        }
1527        assert_eq!(
1528            docs.index("$.customer").expect("there").kind(),
1529            IndexKind::Equality
1530        );
1531
1532        docs.create_ordered_index("$.customer").expect("upgraded");
1533        assert_eq!(docs.indexes().len(), 1, "it replaced rather than added");
1534        assert_eq!(ranged(&docs, Bound::Unbounded, Bound::Unbounded).len(), 8);
1535
1536        // And going the other way leaves the order alone, because an ordered
1537        // index answers equality too.
1538        docs.create_index("$.customer").expect("already there");
1539        assert_eq!(
1540            docs.index("$.customer").expect("there").kind(),
1541            IndexKind::Ordered
1542        );
1543        assert_eq!(docs.indexes().len(), 1);
1544    }
1545
1546    /// A document with a list of tags at `$.tags` and a title at `$.title`.
1547    fn tagged(title: &str, tags: &[&str]) -> Vec<u8> {
1548        let mut b = Builder::new();
1549        b.begin_object().expect("open");
1550        b.key(b"title").expect("key");
1551        b.text(title).expect("value");
1552        b.key(b"tags").expect("key");
1553        b.begin_array().expect("open");
1554        for tag in tags {
1555            b.text(tag).expect("value");
1556        }
1557        b.end_array().expect("close");
1558        b.end_object().expect("close");
1559        b.finish().expect("finished").to_vec()
1560    }
1561
1562    #[test]
1563    fn an_array_index_files_a_document_under_every_element() {
1564        let mut docs = Docs::new();
1565        docs.create_array_index("$.tags").expect("indexed");
1566        docs.put_bytes(b"a", &tagged("one", &["red", "blue"]))
1567            .expect("put");
1568        docs.put_bytes(b"b", &tagged("two", &["blue", "green"]))
1569            .expect("put");
1570        docs.put_bytes(b"c", &tagged("three", &[])).expect("put");
1571
1572        assert_eq!(found(&docs, "$.tags", &Key::text("red")), ["a"]);
1573        assert_eq!(found(&docs, "$.tags", &Key::text("blue")), ["a", "b"]);
1574        assert_eq!(found(&docs, "$.tags", &Key::text("green")), ["b"]);
1575        assert!(found(&docs, "$.tags", &Key::text("puce")).is_empty());
1576        assert_eq!(
1577            docs.index("$.tags").expect("there").len(),
1578            3,
1579            "three distinct tags over two documents"
1580        );
1581    }
1582
1583    #[test]
1584    fn an_array_index_takes_every_element_back_out_again() {
1585        let mut docs = Docs::new();
1586        docs.create_array_index("$.tags").expect("indexed");
1587        docs.put_bytes(b"a", &tagged("one", &["red", "blue"]))
1588            .expect("put");
1589        docs.put_bytes(b"b", &tagged("two", &["blue"]))
1590            .expect("put");
1591
1592        // An overwrite drops one tag and gains another.
1593        docs.put_bytes(b"a", &tagged("one", &["blue", "green"]))
1594            .expect("put");
1595        assert!(found(&docs, "$.tags", &Key::text("red")).is_empty());
1596        assert_eq!(found(&docs, "$.tags", &Key::text("blue")), ["a", "b"]);
1597        assert_eq!(found(&docs, "$.tags", &Key::text("green")), ["a"]);
1598
1599        assert!(docs.remove(b"a"));
1600        assert_eq!(found(&docs, "$.tags", &Key::text("blue")), ["b"]);
1601        assert!(found(&docs, "$.tags", &Key::text("green")).is_empty());
1602        assert_eq!(
1603            docs.index("$.tags").expect("there").len(),
1604            1,
1605            "a tag nobody has left is not a key any more"
1606        );
1607    }
1608
1609    #[test]
1610    fn an_array_index_treats_one_value_as_a_list_of_one() {
1611        let mut docs = Docs::new();
1612        docs.create_array_index("$.status").expect("indexed");
1613        docs.put_bytes(b"order:1", &order(1, "open", 1))
1614            .expect("put");
1615        assert_eq!(found(&docs, "$.status", &Key::text("open")), ["order:1"]);
1616    }
1617
1618    #[test]
1619    fn the_same_element_twice_is_one_posting() {
1620        let mut docs = Docs::new();
1621        docs.create_array_index("$.tags").expect("indexed");
1622        docs.put_bytes(b"a", &tagged("one", &["red", "red", "red"]))
1623            .expect("put");
1624        assert_eq!(found(&docs, "$.tags", &Key::text("red")), ["a"]);
1625        assert_eq!(docs.index("$.tags").expect("there").postings(), 1);
1626
1627        // And taking it out once takes it out, rather than three times over.
1628        assert!(docs.remove(b"a"));
1629        assert_eq!(docs.index("$.tags").expect("there").postings(), 0);
1630        assert!(docs.index("$.tags").expect("there").is_empty());
1631    }
1632
1633    #[test]
1634    fn a_text_index_files_a_document_under_every_word() {
1635        let mut docs = Docs::new();
1636        docs.create_text_index("$.title").expect("indexed");
1637        docs.put_bytes(b"a", &tagged("A red bicycle", &[]))
1638            .expect("put");
1639        docs.put_bytes(b"b", &tagged("The red car, and a bicycle!", &[]))
1640            .expect("put");
1641
1642        assert_eq!(found(&docs, "$.title", &word("bicycle")), ["a", "b"]);
1643        assert_eq!(found(&docs, "$.title", &word("car")), ["b"]);
1644        assert_eq!(
1645            found(&docs, "$.title", &word("RED")),
1646            ["a", "b"],
1647            "a search folds case the same way the write did"
1648        );
1649        assert!(found(&docs, "$.title", &word("lorry")).is_empty());
1650    }
1651
1652    #[test]
1653    fn a_text_index_follows_the_words_through_a_rewrite() {
1654        let mut docs = Docs::new();
1655        docs.create_text_index("$.title").expect("indexed");
1656        docs.put_bytes(b"a", &tagged("a red bicycle", &[]))
1657            .expect("put");
1658        docs.put_bytes(b"a", &tagged("a blue bicycle", &[]))
1659            .expect("put");
1660        assert!(found(&docs, "$.title", &word("red")).is_empty());
1661        assert_eq!(found(&docs, "$.title", &word("blue")), ["a"]);
1662        assert_eq!(found(&docs, "$.title", &word("bicycle")), ["a"]);
1663
1664        assert!(docs.remove(b"a"));
1665        assert!(docs.index("$.title").expect("there").is_empty());
1666    }
1667
1668    #[test]
1669    fn a_text_index_declared_after_the_documents_finds_them() {
1670        let mut docs = Docs::new();
1671        for i in 0..16i64 {
1672            let title = if i % 2 == 0 {
1673                "a red one"
1674            } else {
1675                "a blue one"
1676            };
1677            docs.put_bytes(format!("t:{i}").as_bytes(), &tagged(title, &[]))
1678                .expect("put");
1679        }
1680        docs.create_text_index("$.title").expect("indexed");
1681        assert_eq!(docs.count("$.title", &word("red")).expect("i"), 8);
1682        assert_eq!(docs.count("$.title", &word("one")).expect("i"), 16);
1683        assert_eq!(
1684            docs.index("$.title").expect("there").len(),
1685            4,
1686            "a, red, blue and one"
1687        );
1688    }
1689
1690    #[test]
1691    fn changing_what_an_index_is_asked_rebuilds_it() {
1692        let mut docs = Docs::new();
1693        docs.create_index("$.tags").expect("indexed");
1694        docs.put_bytes(b"a", &tagged("one", &["red", "blue"]))
1695            .expect("put");
1696        assert!(
1697            found(&docs, "$.tags", &Key::text("red")).is_empty(),
1698            "an equality index over an array files nothing"
1699        );
1700
1701        docs.create_array_index("$.tags").expect("rebuilt");
1702        assert_eq!(docs.indexes().len(), 1, "it replaced rather than added");
1703        assert_eq!(found(&docs, "$.tags", &Key::text("red")), ["a"]);
1704
1705        docs.create_array_index("$.tags").expect("already there");
1706        assert_eq!(docs.indexes().len(), 1);
1707    }
1708
1709    /// The key a text index files one word under.
1710    fn word(w: &str) -> Key {
1711        Key::word(w).expect("one word")
1712    }
1713
1714    #[test]
1715    fn a_collection_whose_key_table_is_full_stores_the_rest_with_names() {
1716        // Fill the table with names no document below uses, then write one and
1717        // check it is stored whole rather than refused.
1718        let mut docs = Docs::new();
1719        for i in 0..crate::KEYS_MAX {
1720            let name = format!("filler{i}");
1721            assert!(docs.keys.intern(name.as_bytes()).is_some());
1722        }
1723        assert!(docs.keys().is_full());
1724
1725        docs.put_bytes(b"order:1", &order(1, "open", 1))
1726            .expect("put");
1727        let d = docs.get(b"order:1").expect("stored");
1728        assert!(!d.value().is_interned(), "there were no ids left to use");
1729        assert_eq!(d.get(b"status").and_then(|v| v.as_text()), Some("open"));
1730        assert_eq!(
1731            d.path("$.lines[0].sku")
1732                .expect("a path")
1733                .and_then(|v| v.as_text()),
1734            Some("sku-0")
1735        );
1736    }
1737}