Skip to main content

obj_core/index/
mod.rs

1//! Secondary indexes (L6) — index B+trees layered over the M4 B+tree.
2//!
3//! M7 adds *secondary indexes*: per-collection B+trees keyed by an
4//! order-preserving encoding of one or more document fields, valued
5//! by the document's [`Id`](crate::Id). See `docs/format.md`
6//! § Indexes for the authoritative on-disk layout.
7//!
8//! Each declared index lives in its own B+tree (M4 — no new on-disk
9//! page types). The index's root page-id is persisted inside the
10//! owning [`CollectionDescriptor`](crate::catalog::CollectionDescriptor)
11//! as an [`IndexDescriptor`](crate::catalog::IndexDescriptor) entry
12//! (M5 reserved the field; M7 fills it).
13//!
14//! # Module split
15//!
16//! - [`spec`] — runtime declaration shapes: [`IndexKind`],
17//!   [`IndexSpec`], constructors. The descriptor on-disk shape lives
18//!   in [`crate::catalog`] because the catalog owns the wire row.
19//! - [`key`] — order-preserving byte encoding for `Dynamic` values
20//!   plus the composite envelope. The encoding contract is documented
21//!   in `docs/format.md` § Index key encoding.
22//!
23//! # Power-of-ten posture
24//!
25//! - **Rule 1.** No recursion: the encoder walks the input
26//!   `&[Dynamic]` with a single bounded `for` loop.
27//! - **Rule 2.** Per-encode loops are bounded by the input slice
28//!   length, which the caller has already validated against
29//!   `IndexSpec::key_paths.len()`.
30//! - **Rule 5.** Field count is checked against `IndexSpec`'s
31//!   `key_paths` at the encode-time runtime boundary; type-level
32//!   invariants (kind enum, non-empty `key_paths` for composite,
33//!   single-field for `Each`) sit on the constructors.
34//! - **Rule 7.** No `unwrap` / `expect` on the production path. The
35//!   encoder returns `Err(Error::Codec)` on `String` fields that
36//!   contain an embedded `0x00` (the terminator would split the
37//!   key — see `docs/format.md`).
38//! - **Rule 9.** All dispatch is static — `IndexKind` is an enum
39//!   matched at every call site; no `dyn`, no macro generation.
40
41#![forbid(unsafe_code)]
42
43pub mod extract;
44pub mod key;
45pub mod spec;
46
47pub use crate::index::extract::{extract_index_keys, MAX_EACH_ENTRIES};
48pub use crate::index::key::{
49    encode_field, encode_index_key, encode_index_key_parts, encoded_id_suffix_len, EncodedIndexKey,
50    COMPOSITE_TAG,
51};
52pub use crate::index::spec::{IndexKind, IndexSpec};