use super::super::codec::{
doc_length_key_prefix, field_stats_key_prefix, posting_cluster_positions_key_prefix,
posting_cluster_score_key_prefix, posting_document_key_prefix, posting_key_prefix,
reverse_posting_key_prefix,
};
use super::{keys, other_error, KeyValueBatch, OccurrenceRead, StorageBackendResult};
#[cfg(test)]
mod tests;
pub(super) fn retained_prefixes(
table: &str,
control: &crate::read_control::StorageReadControl,
) -> StorageBackendResult<[uqa_core::memory::BudgetedVec<u8>; 8]> {
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,
};
let tags = [
TAG_POSTING,
TAG_POSTING_CLUSTER_SCORE,
TAG_POSTING_CLUSTER_POSITIONS,
TAG_POSTING_DOCUMENT,
TAG_DOC_LENGTH,
TAG_FIELD_STATS,
TAG_REVERSE_POSTING,
TAG_OCCURRENCE_INDEX,
];
let mut prefixes =
std::array::from_fn(|_| uqa_core::memory::BudgetedVec::new(control.memory()));
for (prefix, tag) in prefixes.iter_mut().zip(tags) {
*prefix = keys::encoding::controlled(table, tag, None, &[], control)?;
}
Ok(prefixes)
}
pub(super) fn legacy_prefixes(table: &str) -> StorageBackendResult<Vec<Vec<u8>>> {
Ok(vec![
posting_key_prefix(table)?,
posting_cluster_score_key_prefix(table)?,
posting_cluster_positions_key_prefix(table)?,
posting_document_key_prefix(table)?,
doc_length_key_prefix(table)?,
field_stats_key_prefix(table)?,
reverse_posting_key_prefix(table)?,
])
}
impl OccurrenceRead<'_> {
pub(super) fn needs_source_rebuild(&self) -> StorageBackendResult<bool> {
self.store.control().check()?;
if let Some(required) = self.format_requires_rebuild.get() {
return Ok(required);
}
let required = self.read_source_rebuild_requirement()?;
self.format_requires_rebuild.set(Some(required));
Ok(required)
}
fn read_source_rebuild_requirement(&self) -> StorageBackendResult<bool> {
let format = self
.store
.get(&keys::kind_prefix(self.table, keys::FORMAT)?)?;
if format.as_deref() == Some(b"source-rebuild") {
return Ok(true);
}
if format
.as_deref()
.is_some_and(|format| format != keys::FORMAT_NAME)
{
return Err(other_error("unsupported occurrence index format"));
}
for prefix in legacy_prefixes(self.table)? {
if self
.store
.contains_prefix_budgeted(&prefix, self.store.control())?
{
return Ok(true);
}
}
if format.is_none()
&& self
.store
.contains_prefix_budgeted(&keys::table_prefix(self.table)?, self.store.control())?
{
return Err(other_error("occurrence index format marker is missing"));
}
Ok(false)
}
pub(super) fn require_graph_format(&self) -> StorageBackendResult<()> {
if self.needs_source_rebuild()? {
return Err(other_error(
"legacy positional data requires an atomic source rebuild",
));
}
Ok(())
}
pub(super) fn clear_index_batch(
&self,
batch: &mut dyn KeyValueBatch,
) -> StorageBackendResult<()> {
batch.reset_occurrences(self.table)?;
batch.delete_prefix(&keys::table_prefix(self.table)?)?;
for prefix in legacy_prefixes(self.table)? {
batch.delete_prefix(&prefix)?;
}
batch.put(
&keys::kind_prefix(self.table, keys::FORMAT)?,
keys::FORMAT_NAME,
)
}
}