Skip to main content

uqa_planner/retrieval_planning/
catalog.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Read-only catalog and retained index interfaces for retrieval costing.
8
9use crate::ColumnStats;
10use std::collections::BTreeMap;
11use uqa_core::{catalog_index::CatalogIndexRow, Edge, Predicate, Vertex};
12use uqa_sql::SQLError;
13
14/// The implementation retains the actual text-index read guard through all analyzer and frequency reads.
15pub trait TextStatisticsRead {
16    fn analyze(&self, field: &str, query: &str) -> Result<Vec<String>, String>;
17    fn doc_freq(&self, field: &str, term: &str) -> Result<u64, String>;
18    fn doc_freq_any_field(&self, term: &str) -> Result<u64, String>;
19    /// Field inventory for analyzed all-field queries. Legacy scalar adapters may omit it.
20    fn field_names(&self) -> Result<Option<Vec<String>>, String> {
21        Ok(None)
22    }
23    /// Estimate all-field query support using each field's retained search revision.
24    fn query_doc_freq_any_field(&self, query: &str) -> Result<u64, String> {
25        let Some(fields) = self.field_names()? else {
26            return self.doc_freq_any_field(query);
27        };
28        let mut frequency = 0_u64;
29        for field in fields {
30            for term in self.analyze_utf16(&field, query)? {
31                frequency = frequency.saturating_add(self.doc_freq_utf16(&field, &term)?);
32            }
33        }
34        Ok(frequency)
35    }
36
37    fn analyze_utf16(&self, field: &str, query: &str) -> Result<Vec<Vec<u16>>, String> {
38        Ok(self
39            .analyze(field, query)?
40            .into_iter()
41            .map(|term| term.encode_utf16().collect())
42            .collect())
43    }
44    fn doc_freq_utf16(&self, field: &str, term: &[u16]) -> Result<u64, String> {
45        self.doc_freq(
46            field,
47            &String::from_utf16(term).map_err(|error| error.to_string())?,
48        )
49    }
50}
51/// The implementation retains the actual vector-index registry read guard.
52pub trait VectorStatisticsRead {
53    fn dimensions(&self, field: &str) -> Option<u32>;
54}
55/// Retain one table generation across text and vector index reads.
56pub trait RetrievalStatisticsTable {
57    fn text_index(&self) -> Box<dyn TextStatisticsRead + '_>;
58    fn vector_indexes(&self) -> Box<dyn VectorStatisticsRead + '_>;
59}
60pub struct GraphStatisticsSnapshot {
61    pub vertices: Vec<Vertex>,
62    pub edges: Vec<Edge>,
63    pub degree_distribution: BTreeMap<u64, u64>,
64    pub vertex_label_counts: BTreeMap<String, u64>,
65}
66pub trait RetrievalPlanningCatalog {
67    fn has_table(&self, table: &str) -> Result<bool, String>;
68    fn resolve_table_name(&self, table: &str) -> Result<Option<String>, String>;
69    fn list_catalog_indexes(&self) -> Result<Vec<CatalogIndexRow>, String>;
70    fn value_index_cardinality(
71        &self,
72        table: &str,
73        field: &str,
74        predicate: &Predicate,
75    ) -> Result<Option<usize>, SQLError>;
76    fn value_index_supports(
77        &self,
78        table: &str,
79        field: &str,
80        predicate: &Predicate,
81    ) -> Result<bool, String>;
82    /// Read analyzed term and indexed-document counts under one retained text-index guard.
83    fn text_top_k_capabilities(
84        &self,
85        table: &str,
86        field: &str,
87        query: &str,
88    ) -> Result<crate::TextTopKCapabilities, SQLError>;
89    fn table_doc_count(&self, table: &str) -> Result<u64, SQLError>;
90    fn try_query_table(
91        &self,
92        table: &str,
93    ) -> Result<Option<Box<dyn RetrievalStatisticsTable>>, String>;
94    fn try_query_column_stats(&self, table: &str) -> Result<BTreeMap<String, ColumnStats>, String>;
95    /// Read all four graph populations under one snapshot before returning.
96    fn graph_snapshot(&self, graph: &str) -> Result<Option<GraphStatisticsSnapshot>, String>;
97}