1use super::{
10 Analyzer, AnalyzerPhase, Arc, BTreeMap, DocId, FieldName, IndexStats, IndexedFieldMetadata,
11 InvertedIndex, KeyValueInvertedIndex, OccurrencePosting, PostingCursor, PostingList,
12 StorageBackendResult, TokenTermKey,
13};
14
15impl InvertedIndex for KeyValueInvertedIndex {
16 fn persisted_block_max_scores_keys_bulk(
17 &self,
18 field: &str,
19 terms: &[TokenTermKey],
20 scorer_fingerprint: &str,
21 ) -> StorageBackendResult<Vec<Option<Vec<f64>>>> {
22 if scorer_fingerprint.is_empty() {
23 return Ok(vec![None; terms.len()]);
24 }
25 self.get_versioned_block_max_scores_keys_bulk(field, terms, scorer_fingerprint)
26 }
27
28 fn persisted_block_max_scores_bulk(
29 &self,
30 field: &str,
31 terms: &[String],
32 scorer_fingerprint: &str,
33 ) -> StorageBackendResult<Vec<Option<Vec<f64>>>> {
34 let terms = terms
35 .iter()
36 .map(|term| TokenTermKey::from_text(term))
37 .collect::<Vec<_>>();
38 self.persisted_block_max_scores_keys_bulk(field, &terms, scorer_fingerprint)
39 }
40
41 fn persisted_block_max_scores(
42 &self,
43 field: &str,
44 term: &str,
45 scorer_fingerprint: &str,
46 ) -> StorageBackendResult<Option<Vec<f64>>> {
47 Ok(self
48 .persisted_block_max_scores_keys_bulk(
49 field,
50 &[TokenTermKey::from_text(term)],
51 scorer_fingerprint,
52 )?
53 .pop()
54 .expect("one term"))
55 }
56
57 fn rebuild_persisted_block_max(
58 &mut self,
59 field: &str,
60 scorer: &dyn crate::block_max_index::BlockMaxScorer,
61 scorer_fingerprint: &str,
62 ) -> StorageBackendResult<bool> {
63 self.rebuild_block_max(field, scorer, scorer_fingerprint)
64 }
65
66 fn posting_read_cursor_key_budgeted<'a>(
67 &'a self,
68 field: &'a str,
69 term: &'a TokenTermKey,
70 control: &crate::read_control::StorageReadControl,
71 ) -> StorageBackendResult<crate::clustered_postings::BudgetedPostingReadCursor<'a>> {
72 control.check()?;
73 crate::clustered_postings::open_controlled_cursor(self.snapshot()?, field, term, control)
74 }
75
76 fn visit_score_clusters(
77 &self,
78 field: &str,
79 term: &TokenTermKey,
80 after: Option<u64>,
81 limit: usize,
82 control: &crate::read_control::StorageReadControl,
83 visit: &mut crate::clustered_postings::ScoreClusterVisitor<'_>,
84 ) -> StorageBackendResult<()> {
85 self.read(|view| view.visit_score_clusters(field, term, after, limit, control, visit))
86 }
87
88 fn get_occurrences_budgeted(
89 &self,
90 doc_id: DocId,
91 field: &str,
92 term: &TokenTermKey,
93 control: &crate::read_control::StorageReadControl,
94 ) -> StorageBackendResult<uqa_core::memory::Budgeted<Vec<uqa_core::TokenOccurrence>>> {
95 self.read(|view| view.get_occurrences_budgeted(doc_id, field, term, control))
96 }
97
98 fn field_stats_scalar_budgeted(
99 &self,
100 field: &str,
101 control: &crate::read_control::StorageReadControl,
102 ) -> StorageBackendResult<IndexStats> {
103 self.read(|view| view.field_stats_scalar_budgeted(field, control))
104 }
105
106 fn analyzer(&self) -> &Analyzer {
107 self.bindings.default_configuration()
108 }
109
110 fn default_analyzer_binding(
111 &self,
112 ) -> StorageBackendResult<crate::inverted_index::AnalyzerDefault> {
113 Ok(self.bindings.default_binding())
114 }
115
116 fn source_rebuild_required(&self) -> StorageBackendResult<bool> {
117 self.read(|view| view.source_rebuild_required())
118 }
119
120 fn add_document(
121 &mut self,
122 doc_id: DocId,
123 fields: BTreeMap<FieldName, String>,
124 ) -> StorageBackendResult<()> {
125 self.add_documents(vec![(doc_id, fields)])
126 }
127
128 fn try_add_documents(
129 &mut self,
130 documents: Vec<(DocId, BTreeMap<FieldName, String>)>,
131 ) -> StorageBackendResult<()> {
132 self.add_documents(documents)
133 }
134
135 fn try_add_documents_observed(
136 &mut self,
137 documents: Vec<(DocId, BTreeMap<FieldName, String>)>,
138 visit: &mut crate::inverted_index::InvertedIndexChangeVisitor<'_>,
139 ) -> StorageBackendResult<()> {
140 self.mutate(|view, batch| view.add_documents(batch, documents, Some(visit)))
141 }
142
143 fn remove_document(&mut self, doc_id: DocId) -> StorageBackendResult<()> {
144 self.add_documents(vec![(doc_id, BTreeMap::new())])
145 }
146
147 fn try_rebuild_documents(
148 &mut self,
149 documents: Vec<(DocId, BTreeMap<FieldName, String>)>,
150 ) -> StorageBackendResult<()> {
151 self.rebuild_documents(documents)
152 }
153
154 fn try_rebuild_documents_cancellable(
155 &mut self,
156 documents: Vec<(DocId, BTreeMap<FieldName, String>)>,
157 cancellation: &uqa_core::CancellationToken,
158 ) -> StorageBackendResult<()> {
159 self.rebuild_documents_inner(documents, Some(cancellation))
160 }
161
162 fn clear(&mut self) -> StorageBackendResult<()> {
163 self.mutate(|view, batch| view.clear_index_batch(batch))
164 }
165
166 fn get_posting_list(&self, field: &str, term: &str) -> StorageBackendResult<PostingList> {
167 self.read(|view| view.get_posting_list(field, term))
168 }
169
170 fn get_posting_list_key(
171 &self,
172 field: &str,
173 term: &TokenTermKey,
174 ) -> StorageBackendResult<PostingList> {
175 self.read(|view| view.get_posting_list_key(field, term))
176 }
177
178 fn posting_cursor(
179 &self,
180 field: &str,
181 term: &str,
182 ) -> StorageBackendResult<Box<dyn PostingCursor>> {
183 self.read(|view| view.posting_cursor(field, term))
184 }
185
186 fn posting_cursor_key(
187 &self,
188 field: &str,
189 term: &TokenTermKey,
190 ) -> StorageBackendResult<Box<dyn PostingCursor>> {
191 self.read(|view| view.posting_cursor_key(field, term))
192 }
193
194 fn get_occurrence_postings(
195 &self,
196 field: &str,
197 term: &TokenTermKey,
198 ) -> StorageBackendResult<Vec<OccurrencePosting>> {
199 self.read(|view| view.get_occurrence_postings(field, term))
200 }
201
202 fn get_occurrences(
203 &self,
204 doc_id: DocId,
205 field: &str,
206 term: &TokenTermKey,
207 ) -> StorageBackendResult<Vec<uqa_core::TokenOccurrence>> {
208 self.read(|view| view.get_occurrences(doc_id, field, term))
209 }
210
211 fn indexed_field_metadata(
212 &self,
213 doc_id: DocId,
214 field: &str,
215 ) -> StorageBackendResult<Option<IndexedFieldMetadata>> {
216 self.read(|view| view.indexed_field_metadata(doc_id, field))
217 }
218
219 fn for_each_term_freq(
220 &self,
221 field: &str,
222 term: &str,
223 visit: &mut dyn FnMut(DocId, u64),
224 ) -> StorageBackendResult<()> {
225 let mut cursor = self.posting_cursor(field, term)?;
226 while let Some(entry) = cursor.current() {
227 visit(entry.doc_id, entry.term_freq);
228 cursor.advance()?;
229 }
230 Ok(())
231 }
232
233 fn doc_freq(&self, field: &str, term: &str) -> StorageBackendResult<u64> {
234 self.read(|view| view.doc_freq(field, term))
235 }
236
237 fn doc_freq_key(&self, field: &str, term: &TokenTermKey) -> StorageBackendResult<u64> {
238 self.read(|view| view.doc_freq_key(field, term))
239 }
240
241 fn get_doc_length(&self, doc_id: DocId, field: &str) -> StorageBackendResult<u64> {
242 self.read(|view| view.get_doc_length(doc_id, field))
243 }
244
245 fn get_scoring_inputs_bulk(
246 &self,
247 doc_ids: &[DocId],
248 field: &str,
249 terms: &[String],
250 ) -> StorageBackendResult<Vec<(u64, Vec<u64>)>> {
251 self.read(|view| view.get_scoring_inputs_bulk(doc_ids, field, terms))
252 }
253
254 fn get_scoring_inputs_keys_bulk(
255 &self,
256 doc_ids: &[DocId],
257 field: &str,
258 terms: &[TokenTermKey],
259 ) -> StorageBackendResult<Vec<(u64, Vec<u64>)>> {
260 self.read(|view| view.get_scoring_inputs_keys_bulk(doc_ids, field, terms))
261 }
262
263 fn get_term_freq(&self, doc_id: DocId, field: &str, term: &str) -> StorageBackendResult<u64> {
264 self.read(|view| view.get_term_freq(doc_id, field, term))
265 }
266
267 fn get_term_freq_key(
268 &self,
269 doc_id: DocId,
270 field: &str,
271 term: &TokenTermKey,
272 ) -> StorageBackendResult<u64> {
273 self.read(|view| view.get_term_freq_key(doc_id, field, term))
274 }
275
276 fn doc_count(&self) -> StorageBackendResult<u64> {
277 self.read(|view| view.doc_count())
278 }
279
280 fn total_field_length(&self, field: &str) -> StorageBackendResult<u64> {
281 self.read(|view| view.total_field_length(field))
282 }
283
284 fn field_doc_count(&self, field: &str) -> StorageBackendResult<u64> {
285 self.read(|view| view.field_doc_count(field))
286 }
287
288 fn vocabulary_terms(&self, field: &str) -> StorageBackendResult<Vec<String>> {
289 self.read(|view| view.vocabulary_terms(field))
290 }
291
292 fn vocabulary_keys(&self, field: &str) -> StorageBackendResult<Vec<TokenTermKey>> {
293 self.read(|view| view.vocabulary_keys(field))
294 }
295
296 fn stats(&self) -> StorageBackendResult<IndexStats> {
297 self.read(|view| view.stats())
298 }
299
300 fn posting_count(&self, field: Option<&str>) -> StorageBackendResult<u64> {
301 self.read(|view| view.posting_count(field))
302 }
303
304 fn doc_length_count(&self, field: Option<&str>) -> StorageBackendResult<u64> {
305 self.read(|view| view.doc_length_count(field))
306 }
307
308 fn term_count(&self, field: Option<&str>) -> StorageBackendResult<u64> {
309 self.read(|view| view.term_count(field))
310 }
311
312 fn snapshot(&self) -> StorageBackendResult<Arc<dyn InvertedIndex>> {
313 self.retained_snapshot()
314 }
315
316 fn field_names(&self) -> StorageBackendResult<Vec<FieldName>> {
317 self.read(|view| view.field_names())
318 }
319
320 fn set_field_analyzer(
321 &mut self,
322 field: &str,
323 analyzer: Analyzer,
324 phase: AnalyzerPhase,
325 ) -> Result<(), String> {
326 self.ensure_writable().map_err(|error| error.to_string())?;
327 let mut candidate = self.bindings.clone();
328 candidate
329 .bind(field, &analyzer, phase)
330 .map_err(|error| error.to_string())?;
331 self.read(|view| view.validate_index_revision_change(field, &candidate))
332 .map_err(|error| error.to_string())?;
333 self.bindings = candidate;
334 Ok(())
335 }
336
337 fn remove_field_analyzers(&mut self, field: &str) -> Result<(), String> {
338 self.ensure_writable().map_err(|error| error.to_string())?;
339 let mut candidate = self.bindings.clone();
340 candidate.remove(field);
341 self.read(|view| view.validate_index_revision_change(field, &candidate))
342 .map_err(|error| error.to_string())?;
343 self.bindings = candidate;
344 Ok(())
345 }
346
347 fn get_field_analyzer(&self, field: &str) -> Analyzer {
348 self.bindings.index_configuration(field).clone()
349 }
350 fn get_search_analyzer(&self, field: &str) -> Analyzer {
351 self.bindings.search_configuration(field).clone()
352 }
353 fn index_analyzer_revision(
354 &self,
355 field: &str,
356 ) -> StorageBackendResult<Arc<uqa_analysis::CompiledAnalyzer>> {
357 Ok(self.bindings.index_revision(field)?)
358 }
359 fn search_analyzer_revision(
360 &self,
361 field: &str,
362 ) -> StorageBackendResult<Arc<uqa_analysis::CompiledAnalyzer>> {
363 Ok(self.bindings.search_revision(field)?)
364 }
365
366 fn set_field_analyzer_revision(
367 &mut self,
368 field: &str,
369 revision: Arc<uqa_analysis::CompiledAnalyzer>,
370 phase: AnalyzerPhase,
371 ) -> Result<(), String> {
372 self.ensure_writable().map_err(|error| error.to_string())?;
373 let mut candidate = self.bindings.clone();
374 candidate
375 .bind_revision(field, revision, phase)
376 .map_err(|error| error.to_string())?;
377 self.read(|view| view.validate_index_revision_change(field, &candidate))
378 .map_err(|error| error.to_string())?;
379 self.bindings = candidate;
380 Ok(())
381 }
382
383 fn set_field_analyzer_revisions(
384 &mut self,
385 field: &str,
386 index: Arc<uqa_analysis::CompiledAnalyzer>,
387 search: Arc<uqa_analysis::CompiledAnalyzer>,
388 ) -> Result<(), String> {
389 self.ensure_writable().map_err(|error| error.to_string())?;
390 let mut candidate = self.bindings.clone();
391 candidate
392 .bind_revisions(field, index, search)
393 .map_err(|error| error.to_string())?;
394 self.read(|view| view.validate_index_revision_change(field, &candidate))
395 .map_err(|error| error.to_string())?;
396 self.bindings = candidate;
397 Ok(())
398 }
399
400 fn rebuild_with_analyzer_revision(
401 &mut self,
402 field: &str,
403 revision: Arc<uqa_analysis::CompiledAnalyzer>,
404 phase: AnalyzerPhase,
405 documents: Vec<(DocId, BTreeMap<FieldName, String>)>,
406 ) -> StorageBackendResult<()> {
407 self.ensure_writable()?;
408 let mut replacement = self.clone();
409 replacement.bindings.bind_revision(field, revision, phase)?;
410 replacement.rebuild_documents(documents)?;
411 *self = replacement;
412 Ok(())
413 }
414
415 fn rebuild_with_analyzer_revision_cancellable(
416 &mut self,
417 field: &str,
418 revision: Arc<uqa_analysis::CompiledAnalyzer>,
419 phase: AnalyzerPhase,
420 documents: Vec<(DocId, BTreeMap<FieldName, String>)>,
421 cancellation: &uqa_core::CancellationToken,
422 ) -> StorageBackendResult<()> {
423 cancellation.check()?;
424 self.ensure_writable()?;
425 let mut replacement = self.clone();
426 replacement.bindings.bind_revision(field, revision, phase)?;
427 replacement.rebuild_documents_inner(documents, Some(cancellation))?;
428 *self = replacement;
429 Ok(())
430 }
431
432 fn get_posting_lists_bulk(
433 &self,
434 field: &str,
435 terms: &[String],
436 ) -> StorageBackendResult<Vec<PostingList>> {
437 self.read(|view| {
438 terms
439 .iter()
440 .map(|term| view.get_posting_list(field, term))
441 .collect()
442 })
443 }
444
445 fn posting_cursors_bulk(
446 &self,
447 field: &str,
448 terms: &[String],
449 ) -> StorageBackendResult<Vec<Box<dyn PostingCursor>>> {
450 self.read(|view| {
451 terms
452 .iter()
453 .map(|term| view.posting_cursor(field, term))
454 .collect()
455 })
456 }
457
458 fn posting_cursors_keys_bulk(
459 &self,
460 field: &str,
461 terms: &[TokenTermKey],
462 ) -> StorageBackendResult<Vec<Box<dyn PostingCursor>>> {
463 self.read(|view| {
464 terms
465 .iter()
466 .map(|term| view.posting_cursor_key(field, term))
467 .collect()
468 })
469 }
470
471 fn get_posting_lists_keys_bulk(
472 &self,
473 field: &str,
474 terms: &[TokenTermKey],
475 ) -> StorageBackendResult<Vec<PostingList>> {
476 self.read(|view| {
477 terms
478 .iter()
479 .map(|term| view.get_posting_list_key(field, term))
480 .collect()
481 })
482 }
483
484 fn field_stats(&self, field: &str) -> StorageBackendResult<IndexStats> {
485 self.read(|view| {
486 let mut stats = view.stats()?;
487 let field_docs = view.field_doc_count(field)?;
488 stats.total_docs = field_docs;
489 stats.avg_doc_length = if field_docs > 0 {
490 view.total_field_length(field)? as f64 / field_docs as f64
491 } else {
492 0.0
493 };
494 Ok(stats)
495 })
496 }
497
498 fn field_stats_scalar(&self, field: &str) -> StorageBackendResult<IndexStats> {
499 self.read(|view| {
500 let mut stats = IndexStats::default();
501 let field_docs = view.field_doc_count(field)?;
502 stats.total_docs = field_docs;
503 stats.avg_doc_length = if field_docs > 0 {
504 view.total_field_length(field)? as f64 / field_docs as f64
505 } else {
506 0.0
507 };
508 Ok(stats)
509 })
510 }
511
512 fn get_posting_list_any_field(&self, term: &str) -> StorageBackendResult<PostingList> {
513 self.read(|view| {
514 let mut result = PostingList::new();
515 for field in view.field_names()? {
516 let pl = view.get_posting_list(&field, term)?;
517 result = result.merge_union(&pl);
518 }
519 Ok(result)
520 })
521 }
522
523 fn doc_freq_any_field(&self, term: &str) -> StorageBackendResult<u64> {
524 self.read(|view| {
525 let mut total = 0_u64;
526 for field in view.field_names()? {
527 total = total
528 .checked_add(view.doc_freq(&field, term)?)
529 .ok_or_else(|| super::other_error("document frequency overflow"))?;
530 }
531 Ok(total)
532 })
533 }
534
535 fn get_total_doc_length(&self, doc_id: DocId) -> StorageBackendResult<u64> {
536 self.read(|view| {
537 let mut total = 0_u64;
538 for field in view.field_names()? {
539 total = total
540 .checked_add(view.get_doc_length(doc_id, &field)?)
541 .ok_or_else(|| super::other_error("document length overflow"))?;
542 }
543 Ok(total)
544 })
545 }
546
547 fn get_total_term_freq(&self, doc_id: DocId, term: &str) -> StorageBackendResult<u64> {
548 self.read(|view| {
549 let mut total = 0_u64;
550 for field in view.field_names()? {
551 total = total
552 .checked_add(view.get_term_freq(doc_id, &field, term)?)
553 .ok_or_else(|| super::other_error("term frequency overflow"))?;
554 }
555 Ok(total)
556 })
557 }
558
559 fn get_doc_lengths_bulk(
560 &self,
561 doc_ids: &[DocId],
562 field: &str,
563 ) -> StorageBackendResult<BTreeMap<DocId, u64>> {
564 self.read(|view| {
565 let mut out = BTreeMap::new();
566 for doc_id in doc_ids {
567 out.insert(*doc_id, view.get_doc_length(*doc_id, field)?);
568 }
569 Ok(out)
570 })
571 }
572
573 fn get_term_freqs_bulk(
574 &self,
575 doc_ids: &[DocId],
576 field: &str,
577 term: &str,
578 ) -> StorageBackendResult<BTreeMap<DocId, u64>> {
579 self.read(|view| {
580 let mut out = BTreeMap::new();
581 for doc_id in doc_ids {
582 out.insert(*doc_id, view.get_term_freq(*doc_id, field, term)?);
583 }
584 Ok(out)
585 })
586 }
587}