uqa_storage/key_value/inverted_index/
view.rs1use super::{AnalyzerBindings, Arc, DocId, FieldName, InvertedIndex, KeyValueInvertedIndex};
10use crate::key_value::{index_view, KeyValueBatch, KeyValueRead, OccurrenceStorage};
11use crate::StorageBackendResult;
12use uqa_core::memory::BudgetedVec;
13
14type Entry = (BudgetedVec<u8>, BudgetedVec<u8>);
15
16#[derive(Clone)]
17pub(super) enum OccurrenceSource {
18 Live(Arc<dyn OccurrenceStorage>),
19 Retained(Arc<dyn KeyValueRead + Send + Sync>),
20}
21
22pub(super) struct OccurrenceRead<'a> {
23 pub(super) store: &'a dyn KeyValueRead,
24 pub(super) table: &'a str,
25 pub(super) bindings: &'a AnalyzerBindings,
26 pub(super) format_requires_rebuild: std::cell::Cell<Option<bool>>,
27}
28
29impl OccurrenceRead<'_> {
30 pub(super) fn scan_prefix(&self, prefix: &[u8]) -> StorageBackendResult<BudgetedVec<Entry>> {
31 let mut rows = BudgetedVec::new(self.store.control().memory());
32 append_rows(self.store, prefix, &mut rows)?;
33 Ok(rows)
34 }
35}
36
37fn append_rows(
38 read: &dyn KeyValueRead,
39 prefix: &[u8],
40 rows: &mut BudgetedVec<Entry>,
41) -> StorageBackendResult<()> {
42 read.visit_prefix(prefix, &mut |key, value| {
43 read.control().check()?;
44 rows.reserve(1)?;
45 let mut owned_key = BudgetedVec::new(read.control().memory());
46 owned_key.extend_from_slice(key)?;
47 let mut owned_value = BudgetedVec::new(read.control().memory());
48 owned_value.extend_from_slice(value)?;
49 rows.push((owned_key, owned_value))?;
50 Ok(())
51 })
52}
53
54impl KeyValueInvertedIndex {
55 pub(super) fn read<T>(
56 &self,
57 operation: impl FnOnce(OccurrenceRead<'_>) -> StorageBackendResult<T>,
58 ) -> StorageBackendResult<T> {
59 let evaluate = |store: &dyn KeyValueRead| {
60 operation(OccurrenceRead {
61 store,
62 table: &self.table,
63 bindings: &self.bindings,
64 format_requires_rebuild: std::cell::Cell::new(None),
65 })
66 };
67 match &self.source {
68 OccurrenceSource::Live(store) => {
69 index_view::read_scope(|operation| store.read(operation), evaluate)
70 }
71 OccurrenceSource::Retained(read) => {
72 read.control().check()?;
73 evaluate(read.as_ref())
74 }
75 }
76 }
77
78 pub(super) fn ensure_writable(&self) -> StorageBackendResult<()> {
79 match self.source {
80 OccurrenceSource::Live(_) => Ok(()),
81 OccurrenceSource::Retained(_) => {
82 Err(super::other_error("inverted-index snapshots are read-only"))
83 }
84 }
85 }
86
87 pub(super) fn mutate(
88 &self,
89 operation: impl FnOnce(&OccurrenceRead<'_>, &mut dyn KeyValueBatch) -> StorageBackendResult<()>,
90 ) -> StorageBackendResult<()> {
91 self.ensure_writable()?;
92 let OccurrenceSource::Live(store) = &self.source else {
93 unreachable!()
94 };
95 index_view::mutation_scope(
96 |operation| store.mutate(operation),
97 |read, batch| {
98 operation(
99 &OccurrenceRead {
100 store: read,
101 table: &self.table,
102 bindings: &self.bindings,
103 format_requires_rebuild: std::cell::Cell::new(None),
104 },
105 batch,
106 )
107 },
108 )
109 }
110
111 pub(super) fn add_documents(
112 &self,
113 documents: Vec<(DocId, std::collections::BTreeMap<FieldName, String>)>,
114 ) -> StorageBackendResult<()> {
115 self.mutate(|view, batch| view.add_documents(batch, documents, None))
116 }
117
118 pub(super) fn rebuild_documents(
119 &self,
120 source: &mut dyn crate::inverted_index::TextIndexSource,
121 ) -> StorageBackendResult<()> {
122 self.rebuild_documents_inner(source, None)
123 }
124
125 pub(super) fn rebuild_documents_inner(
127 &self,
128 source: &mut dyn crate::inverted_index::TextIndexSource,
129 cancellation: Option<&uqa_core::CancellationToken>,
130 ) -> StorageBackendResult<()> {
131 self.ensure_writable()?;
132 let control = self.read(|view| Ok(view.store.control().clone()))?;
133 let staged = super::rebuild::stage_source(&self.bindings, &control, source, cancellation)?;
134 self.mutate(|view, batch| view.write_rebuild(batch, &staged, cancellation))
135 }
136
137 pub fn snapshot_from_read(
139 read: &dyn KeyValueRead,
140 table: &str,
141 bindings: &AnalyzerBindings,
142 ) -> StorageBackendResult<Arc<dyn InvertedIndex>> {
143 read.control().check()?;
144 let prefixes = super::format::retained_prefixes(table, read.control())?;
145 let borrowed: [&[u8]; 8] = std::array::from_fn(|slot| &prefixes[slot][..]);
146 let source = read.retain(&borrowed)?;
147 Self::snapshot_from_source(source, table, bindings)
148 }
149
150 pub(super) fn retained_snapshot(&self) -> StorageBackendResult<Arc<dyn InvertedIndex>> {
151 match &self.source {
152 OccurrenceSource::Retained(source) => {
153 Self::snapshot_from_source(Arc::clone(source), &self.table, &self.bindings)
154 }
155 OccurrenceSource::Live(_) => {
156 self.read(|view| Self::snapshot_from_read(view.store, view.table, view.bindings))
157 }
158 }
159 }
160
161 fn snapshot_from_source(
162 source: Arc<dyn KeyValueRead + Send + Sync>,
163 table: &str,
164 bindings: &AnalyzerBindings,
165 ) -> StorageBackendResult<Arc<dyn InvertedIndex>> {
166 use crate::ReadOnlySnapshot;
167 use uqa_core::memory::BudgetedString;
168
169 let control = source.control().clone();
170 control.check()?;
171 let bindings = bindings.retained(&control)?;
172 let mut name = BudgetedString::new(control.memory());
173 name.reserve(table.len())?;
174 for (offset, character) in table.chars().enumerate() {
175 if offset % 1024 == 0 {
176 control.check()?;
177 }
178 name.push(character)?;
179 }
180 let (table, mut memory) = name.into_parts();
181 memory.grow(size_of::<Self>())?;
182 memory.grow(size_of::<ReadOnlySnapshot<dyn InvertedIndex>>())?;
183 let snapshot: Arc<dyn InvertedIndex> = Arc::new(Self {
184 source: OccurrenceSource::Retained(source),
185 table,
186 bindings,
187 });
188 let snapshot = ReadOnlySnapshot::with_retention(snapshot, memory)?
189 .with_inverted_read_control(&control)?;
190 Ok(Arc::new(snapshot))
191 }
192}