Skip to main content

khive_storage/
text.rs

1//! Full-text search capability.
2
3use async_trait::async_trait;
4use uuid::Uuid;
5
6use crate::capability::StorageCapability;
7use crate::error::StorageError;
8use crate::types::{
9    BatchWriteSummary, IndexRebuildScope, StorageResult, TextDocument, TextFilter, TextIndexStats,
10    TextSearchHit, TextSearchOptions, TextSearchRequest, TextTermStats, TextTermStatsRequest,
11};
12
13/// Full-text search capability over indexed documents.
14#[async_trait]
15pub trait TextSearch: Send + Sync + 'static {
16    /// Index or update a single text document.
17    async fn upsert_document(&self, document: TextDocument) -> StorageResult<()>;
18    /// Index or update a batch of text documents.
19    async fn upsert_documents(
20        &self,
21        documents: Vec<TextDocument>,
22    ) -> StorageResult<BatchWriteSummary>;
23    /// Remove a document from the text index.
24    async fn delete_document(&self, namespace: &str, subject_id: Uuid) -> StorageResult<bool>;
25    /// Fetch an indexed document by namespace and subject ID.
26    async fn get_document(
27        &self,
28        namespace: &str,
29        subject_id: Uuid,
30    ) -> StorageResult<Option<TextDocument>>;
31    /// Run a full-text search query and return ranked hits.
32    async fn search(&self, request: TextSearchRequest) -> StorageResult<Vec<TextSearchHit>>;
33    /// Count documents matching a filter.
34    async fn count(&self, filter: TextFilter) -> StorageResult<u64>;
35    /// Return index metadata and health statistics.
36    async fn stats(&self) -> StorageResult<TextIndexStats>;
37    /// Rebuild the text index, optionally scoped to a subset of entries.
38    async fn rebuild(&self, scope: IndexRebuildScope) -> StorageResult<TextIndexStats>;
39
40    /// Search with explicit gather options (candidate-gather optimization).
41    ///
42    /// Default delegates to `search` when options are default (Ranked mode).
43    /// Backends that do not implement this return `StorageError::Unsupported`
44    /// for non-default options, which the caller must handle by falling back
45    /// or propagating.
46    async fn search_with_options(
47        &self,
48        request: TextSearchRequest,
49        options: TextSearchOptions,
50    ) -> StorageResult<Vec<TextSearchHit>> {
51        if options == TextSearchOptions::default() {
52            self.search(request).await
53        } else {
54            Err(StorageError::Unsupported {
55                capability: StorageCapability::Text,
56                operation: "search_with_options".into(),
57                message: "this backend does not implement non-default gather options".into(),
58            })
59        }
60    }
61
62    /// Return per-term document frequency and IDF for a set of query terms.
63    ///
64    /// Default returns `StorageError::Unsupported`. Only FTS5-backed stores
65    /// provide a concrete implementation.
66    async fn term_stats(
67        &self,
68        _request: TextTermStatsRequest,
69    ) -> StorageResult<Vec<TextTermStats>> {
70        Err(StorageError::Unsupported {
71            capability: StorageCapability::Text,
72            operation: "term_stats".into(),
73            message: "this backend does not implement term_stats".into(),
74        })
75    }
76}