uqa-storage 0.3.6

Document store, inverted index, IVF/HNSW vectors, B-tree, R*Tree, catalog
Documentation
//
// Unified Query Algebra
//
// Copyright (c) 2023-2026 Cognica, Inc.
//

//! Bounded provider keys, borrowed reads and selected-document graph decoding.

use super::{keys, other_error, FieldStats, IndexedFieldMetadata, KeyValueInvertedIndex};
use crate::clustered_postings::{
    cluster_id, decode_occurrence_document_budgeted, EncodedScoreClusterRef, ScoreClusterVisitor,
};
use crate::key_value::{
    TAG_DOC_LENGTH, TAG_FIELD_STATS, TAG_OCCURRENCE_INDEX, TAG_POSTING,
    TAG_POSTING_CLUSTER_POSITIONS, TAG_POSTING_CLUSTER_SCORE, TAG_POSTING_DOCUMENT,
    TAG_REVERSE_POSTING,
};
use crate::{read_control::StorageReadControl, StorageBackendResult, TokenTermKey};
use keys::encoding::{controlled, Part, Part::Number, Part::Segment};
use uqa_core::{
    memory::{Budgeted, BudgetedVec},
    DocId, IndexStats, TokenOccurrence,
};

impl KeyValueInvertedIndex {
    fn read_key(
        &self,
        kind: u8,
        tail: &[Part<'_>],
        control: &StorageReadControl,
    ) -> StorageBackendResult<BudgetedVec<u8>> {
        controlled(&self.table, TAG_OCCURRENCE_INDEX, Some(kind), tail, control)
    }

    pub(super) fn require_graph_format_budgeted(
        &self,
        control: &StorageReadControl,
    ) -> StorageBackendResult<()> {
        let mut marker = false;
        {
            let key = self.read_key(keys::FORMAT, &[], control)?;
            self.store.visit_value(&key, control, &mut |bytes| {
                marker = bytes.is_some();
                if bytes.is_some_and(|bytes| bytes != keys::FORMAT_NAME) {
                    return Err(other_error("unsupported occurrence index format"));
                }
                Ok(())
            })?;
        }
        for tag in [
            TAG_POSTING,
            TAG_POSTING_CLUSTER_SCORE,
            TAG_POSTING_CLUSTER_POSITIONS,
            TAG_POSTING_DOCUMENT,
            TAG_DOC_LENGTH,
            TAG_FIELD_STATS,
            TAG_REVERSE_POSTING,
        ] {
            let prefix = controlled(&self.table, tag, None, &[], control)?;
            if self.store.contains_prefix_budgeted(&prefix, control)? {
                return Err(other_error(
                    "legacy positional data requires an atomic source rebuild",
                ));
            }
        }
        if !marker {
            let prefix = controlled(&self.table, TAG_OCCURRENCE_INDEX, None, &[], control)?;
            if self.store.contains_prefix_budgeted(&prefix, control)? {
                return Err(other_error("occurrence index format marker is missing"));
            }
        }
        control.check()
    }

    pub(super) fn visit_clusters_budgeted(
        &self,
        field: &str,
        term: &TokenTermKey,
        after: Option<u64>,
        limit: usize,
        control: &StorageReadControl,
        visit: &mut ScoreClusterVisitor<'_>,
    ) -> StorageBackendResult<()> {
        self.require_graph_format_budgeted(control)?;
        let prefix = self.read_key(
            keys::SCORE,
            &[Segment(field.as_bytes()), Segment(term.as_bytes())],
            control,
        )?;
        let after = after
            .map(|cluster| {
                self.read_key(
                    keys::SCORE,
                    &[
                        Segment(field.as_bytes()),
                        Segment(term.as_bytes()),
                        Number(cluster),
                    ],
                    control,
                )
            })
            .transpose()?;
        self.store.visit_prefix_after(
            &prefix,
            after.as_deref(),
            limit,
            control,
            &mut |key, bytes| {
                control.check()?;
                let suffix = key
                    .strip_prefix(&*prefix)
                    .ok_or_else(|| other_error("score source returned an unrelated key"))?;
                let cluster_id = u64::from_be_bytes(
                    suffix
                        .try_into()
                        .map_err(|_| other_error("invalid occurrence cluster key"))?,
                );
                visit(EncodedScoreClusterRef {
                    cluster_id,
                    stored_count: None,
                    bytes,
                })
            },
        )
    }

    fn field_stats_budgeted(
        &self,
        field: &str,
        control: &StorageReadControl,
    ) -> StorageBackendResult<Option<FieldStats>> {
        let key = self.read_key(keys::FIELD, &[Segment(field.as_bytes())], control)?;
        let mut stats = None;
        self.store.visit_value(&key, control, &mut |bytes| {
            stats = bytes.map(FieldStats::from_bytes).transpose()?;
            Ok(())
        })?;
        Ok(stats)
    }

    pub(super) fn scalar_stats_budgeted(
        &self,
        field: &str,
        control: &StorageReadControl,
    ) -> StorageBackendResult<IndexStats> {
        self.require_graph_format_budgeted(control)?;
        let mut stats = IndexStats::default();
        if let Some(field) = self.field_stats_budgeted(field, control)? {
            stats.total_docs = field.doc_count;
            stats.avg_doc_length = field.total_length as f64 / field.doc_count as f64;
        }
        control.check()?;
        Ok(stats)
    }

    pub(super) fn occurrences_budgeted(
        &self,
        doc_id: DocId,
        field: &str,
        term: &TokenTermKey,
        control: &StorageReadControl,
    ) -> StorageBackendResult<Budgeted<Vec<TokenOccurrence>>> {
        self.require_graph_format_budgeted(control)?;
        let cluster = cluster_id(doc_id);
        let mut scores = None;
        {
            let key = self.read_key(
                keys::SCORE,
                &[
                    Segment(field.as_bytes()),
                    Segment(term.as_bytes()),
                    Number(cluster),
                ],
                control,
            )?;
            self.store.visit_value(&key, control, &mut |bytes| {
                if let Some(bytes) = bytes {
                    let mut copy = BudgetedVec::new(control.memory());
                    copy.reserve(bytes.len())?;
                    for (index, byte) in bytes.iter().copied().enumerate() {
                        if index % 1024 == 0 {
                            control.check()?;
                        }
                        copy.push(byte)?;
                    }
                    scores = Some(copy);
                }
                Ok(())
            })?;
        }
        let mut posting = None;
        {
            let key = self.read_key(
                keys::POSITIONS,
                &[
                    Segment(field.as_bytes()),
                    Segment(term.as_bytes()),
                    Number(cluster),
                ],
                control,
            )?;
            self.store.visit_value(&key, control, &mut |positions| {
                match (scores.as_deref(), positions) {
                    (None, None) => {}
                    (Some(scores), Some(positions)) => {
                        posting = decode_occurrence_document_budgeted(
                            cluster,
                            scores,
                            positions,
                            doc_id,
                            control.memory(),
                            || control.check(),
                        )?;
                    }
                    _ => return Err(other_error("occurrence score and graph payloads disagree")),
                }
                Ok(())
            })?;
        }
        drop(scores);
        let Some(posting) = posting else {
            return Ok(Budgeted::new(
                Vec::new(),
                control.memory().empty_reservation(),
            ));
        };
        let mut metadata = None;
        {
            let key = self.read_key(
                keys::METADATA,
                &[Segment(field.as_bytes()), Number(doc_id)],
                control,
            )?;
            self.store.visit_value(&key, control, &mut |bytes| {
                metadata = bytes.map(IndexedFieldMetadata::from_bytes).transpose()?;
                Ok(())
            })?;
        }
        let metadata =
            metadata.ok_or_else(|| other_error("occurrence source metadata is missing"))?;
        let stats = self
            .field_stats_budgeted(field, control)?
            .ok_or_else(|| other_error("indexed field revision is missing"))?;
        if stats.revision != metadata.revision() {
            return Err(other_error("indexed field revisions disagree"));
        }
        metadata.validate_posting(&posting, || control.check())?;
        control.check()?;
        let (posting, memory) = posting.into_parts();
        Ok(Budgeted::new(posting.occurrences, memory))
    }
}