Skip to main content

relay_knowledge/storage/contracts/
search.rs

1use std::ops::Deref;
2
3use crate::domain::{GraphVersion, RetrievalHit, RetrieverSource, TraversalProvenanceTrace};
4
5/// Maximum Unicode scalar values accepted by every graph-search adapter.
6pub const MAX_GRAPH_SEARCH_QUERY_CHARS: usize = 10_000;
7/// Maximum lexical terms admitted to the FTS5 query builder.
8pub const MAX_GRAPH_SEARCH_FTS_TOKENS: usize = 128;
9/// Conservative tokenizer-work bound when Unicode category rules split a phrase.
10pub const MAX_GRAPH_SEARCH_FTS_CODEPOINTS: usize = 1_024;
11/// Maximum UTF-8 bytes in one lexical term admitted to FTS5.
12pub const MAX_GRAPH_SEARCH_TOKEN_BYTES: usize = 128;
13/// Maximum candidate limit accepted by the SQLite graph-search implementation.
14pub const MAX_GRAPH_SEARCH_LIMIT: usize = 1_000;
15
16/// Bounded graph search request against an explicit graph snapshot.
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct GraphSearchRequest {
19    pub query: String,
20    pub source_scope: Option<String>,
21    pub graph_version: GraphVersion,
22    pub limit: usize,
23    pub disabled_retriever_sources: Vec<RetrieverSource>,
24}
25
26impl GraphSearchRequest {
27    /// Returns whether storage may execute a retriever family for this request.
28    pub fn allows_retriever_source(&self, source: RetrieverSource) -> bool {
29        !self.disabled_retriever_sources.contains(&source)
30    }
31
32    /// Maximum provenance items exposed for this bounded search.
33    pub fn max_trace_items(&self) -> usize {
34        self.limit
35            .saturating_mul(4)
36            .max(self.limit.saturating_add(8))
37    }
38}
39
40/// Search hits plus the bounded traversal trace that produced them.
41#[derive(Debug, Clone, PartialEq)]
42pub struct GraphSearchOutcome {
43    pub hits: Vec<RetrievalHit>,
44    pub trace: TraversalProvenanceTrace,
45}
46
47impl GraphSearchOutcome {
48    /// Builds a trace from already-ranked hits for simple stores and test doubles.
49    pub fn from_hits(request: &GraphSearchRequest, hits: Vec<RetrievalHit>) -> Self {
50        let mut trace = TraversalProvenanceTrace::from_hits(
51            request.graph_version,
52            request.source_scope.clone(),
53            routed_intent(&request.query),
54            &hits,
55        );
56        trace.apply_budget(request.max_trace_items());
57
58        Self { hits, trace }
59    }
60}
61
62impl Deref for GraphSearchOutcome {
63    type Target = [RetrievalHit];
64
65    fn deref(&self) -> &Self::Target {
66        &self.hits
67    }
68}
69
70fn routed_intent(query: &str) -> String {
71    if query.split_whitespace().count() <= 3 {
72        "direct_context_lookup".to_owned()
73    } else {
74        "multi_term_context_lookup".to_owned()
75    }
76}
77
78#[cfg(test)]
79#[path = "search_tests.rs"]
80mod tests;