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.
//

//! A table-owned namespace for complete occurrence indexes, separate from legacy scalar keys.

use super::codec::{other_error, read_segment, read_str, read_u64};
use super::{DocId, StorageBackendResult, TAG_OCCURRENCE_INDEX};
use crate::TokenTermKey;

pub(super) mod encoding;
use encoding::Part::{Number, Segment};

pub(super) const SCORE: u8 = b's';
pub(super) const POSITIONS: u8 = b'p';
pub(super) const DOCUMENT: u8 = b'd';
pub(super) const LENGTH: u8 = b'l';
pub(super) const METADATA: u8 = b'm';
pub(super) const FIELD: u8 = b'f';
pub(super) const FORMAT: u8 = b'v';
pub(super) const FORMAT_NAME: &[u8] = b"occurrences-v2";

pub(super) fn table_prefix(table: &str) -> StorageBackendResult<Vec<u8>> {
    encoding::key(table, TAG_OCCURRENCE_INDEX, None, &[])
}

pub(super) fn kind_prefix(table: &str, kind: u8) -> StorageBackendResult<Vec<u8>> {
    encoding::key(table, TAG_OCCURRENCE_INDEX, Some(kind), &[])
}

pub(super) fn field_prefix(table: &str, kind: u8, field: &str) -> StorageBackendResult<Vec<u8>> {
    encoding::key(
        table,
        TAG_OCCURRENCE_INDEX,
        Some(kind),
        &[Segment(field.as_bytes())],
    )
}

pub(super) fn term_prefix(
    table: &str,
    kind: u8,
    field: &str,
    term: &TokenTermKey,
) -> StorageBackendResult<Vec<u8>> {
    encoding::key(
        table,
        TAG_OCCURRENCE_INDEX,
        Some(kind),
        &[Segment(field.as_bytes()), Segment(term.as_bytes())],
    )
}

pub(super) fn cluster_key(
    table: &str,
    kind: u8,
    field: &str,
    term: &TokenTermKey,
    cluster: u64,
) -> StorageBackendResult<Vec<u8>> {
    encoding::key(
        table,
        TAG_OCCURRENCE_INDEX,
        Some(kind),
        &[
            Segment(field.as_bytes()),
            Segment(term.as_bytes()),
            Number(cluster),
        ],
    )
}

pub(super) fn document_prefix(
    table: &str,
    kind: u8,
    doc_id: DocId,
) -> StorageBackendResult<Vec<u8>> {
    encoding::key(table, TAG_OCCURRENCE_INDEX, Some(kind), &[Number(doc_id)])
}

pub(super) fn document_key(
    table: &str,
    kind: u8,
    doc_id: DocId,
    field: &str,
) -> StorageBackendResult<Vec<u8>> {
    encoding::key(
        table,
        TAG_OCCURRENCE_INDEX,
        Some(kind),
        &[Number(doc_id), Segment(field.as_bytes())],
    )
}

pub(super) fn metadata_key(
    table: &str,
    field: &str,
    doc_id: DocId,
) -> StorageBackendResult<Vec<u8>> {
    encoding::key(
        table,
        TAG_OCCURRENCE_INDEX,
        Some(METADATA),
        &[Segment(field.as_bytes()), Number(doc_id)],
    )
}

fn header(key: &[u8], kind: u8) -> StorageBackendResult<usize> {
    if key.first() != Some(&TAG_OCCURRENCE_INDEX) {
        return Err(other_error("invalid occurrence index key"));
    }
    let mut offset = 1;
    read_str(key, &mut offset)?;
    if key.get(offset) != Some(&kind) {
        return Err(other_error("invalid occurrence key kind"));
    }
    Ok(offset + 1)
}

fn end(key: &[u8], offset: usize) -> StorageBackendResult<()> {
    if key.len() != offset {
        return Err(other_error("trailing bytes in occurrence index key"));
    }
    Ok(())
}

pub(super) fn read_cluster(
    key: &[u8],
    kind: u8,
) -> StorageBackendResult<(String, TokenTermKey, u64)> {
    let mut offset = header(key, kind)?;
    let field = read_str(key, &mut offset)?;
    let term = TokenTermKey::from_bytes(read_segment(key, &mut offset)?.to_vec())?;
    let cluster = read_u64(key, &mut offset)?;
    end(key, offset)?;
    Ok((field, term, cluster))
}

pub(super) fn read_document(key: &[u8], kind: u8) -> StorageBackendResult<(DocId, String)> {
    let mut offset = header(key, kind)?;
    let doc_id = read_u64(key, &mut offset)?;
    let field = read_str(key, &mut offset)?;
    end(key, offset)?;
    Ok((doc_id, field))
}

pub(super) fn read_field(key: &[u8]) -> StorageBackendResult<String> {
    let mut offset = header(key, FIELD)?;
    let field = read_str(key, &mut offset)?;
    end(key, offset)?;
    Ok(field)
}