Skip to main content

relay_knowledge/indexing/
mod.rs

1//! Index refresh planning for derived read models.
2//!
3//! This module owns index-family selection rules. Concrete index writers remain
4//! outside the domain model and update only derived metadata/read models.
5
6use crate::domain::IndexKind;
7
8/// Deduplicated index refresh plan.
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct IndexRefreshPlan {
11    kinds: Vec<IndexKind>,
12}
13
14impl IndexRefreshPlan {
15    /// Builds a refresh plan. An empty request means all v1 index families.
16    pub fn from_requested(kinds: Vec<IndexKind>) -> Self {
17        if kinds.is_empty() {
18            return Self {
19                kinds: IndexKind::ALL.to_vec(),
20            };
21        }
22
23        let mut deduped = Vec::new();
24        for kind in kinds {
25            if !deduped.contains(&kind) {
26                deduped.push(kind);
27            }
28        }
29
30        Self { kinds: deduped }
31    }
32
33    /// Consumes the plan into index kinds in refresh order.
34    pub fn into_kinds(self) -> Vec<IndexKind> {
35        self.kinds
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    #[test]
44    fn empty_request_refreshes_all_index_families() {
45        let plan = IndexRefreshPlan::from_requested(Vec::new());
46
47        assert_eq!(plan.into_kinds(), IndexKind::ALL);
48    }
49
50    #[test]
51    fn duplicate_index_kinds_are_removed_in_order() {
52        let plan = IndexRefreshPlan::from_requested(vec![
53            IndexKind::Vector,
54            IndexKind::Vector,
55            IndexKind::Bm25,
56        ]);
57
58        assert_eq!(plan.into_kinds(), [IndexKind::Vector, IndexKind::Bm25]);
59    }
60}