Skip to main content

max / goingson

4.9 KB · 162 lines History Blame Raw
1 use super::*;
2
3 /// Type of item in search results.
4 #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
5 #[serde(rename_all = "lowercase")]
6 pub enum SearchResultType {
7 /// A task item.
8 Task,
9 /// An email item.
10 Email,
11 /// A project item.
12 Project,
13 /// A calendar event.
14 Event,
15 /// A contact.
16 Contact,
17 }
18
19 /// A single search result item with ranking information.
20 #[derive(Debug, Clone, serde::Serialize)]
21 pub struct SearchResultItem {
22 /// Item ID.
23 pub id: Uuid,
24 /// Type of the result.
25 pub result_type: SearchResultType,
26 /// Display title.
27 pub title: String,
28 /// Text snippet showing match context.
29 pub snippet: Option<String>,
30 /// Associated project ID if any.
31 pub project_id: Option<ProjectId>,
32 /// Associated project name if any.
33 pub project_name: Option<String>,
34 /// Relevance rank (higher is better).
35 pub rank: f64,
36 }
37
38 /// Search query with filtering and pagination options.
39 #[derive(Debug, Clone, Default)]
40 pub struct SearchQuery {
41 /// The search terms (FTS text after filters extracted).
42 pub query: String,
43 /// Filter to specific result types.
44 pub types: Option<Vec<SearchResultType>>,
45 /// Filter to a specific project by ID.
46 pub project_id: Option<ProjectId>,
47 /// Filter to a specific project by name (partial match).
48 pub project_name: Option<String>,
49 /// Filter to items on or after this date.
50 pub date_from: Option<DateTime<Utc>>,
51 /// Filter to items on or before this date.
52 pub date_to: Option<DateTime<Utc>>,
53 /// Maximum results to return.
54 pub limit: Option<i64>,
55 /// Offset for pagination.
56 pub offset: Option<i64>,
57 /// `is:` filters for time/state conditions.
58 pub is_filters: Vec<crate::search_parser::IsFilter>,
59 /// Priority filter (`priority:high` etc).
60 pub priority: Option<crate::models::Priority>,
61 /// Tags to include (`tag:name`).
62 pub tags_include: Vec<String>,
63 /// Tags to exclude (`-tag:name`).
64 pub tags_exclude: Vec<String>,
65 }
66
67 impl SearchQuery {
68 /// Creates a new search query with default options.
69 pub fn new(query: impl Into<String>) -> Self {
70 Self {
71 query: query.into(),
72 types: None,
73 project_id: None,
74 project_name: None,
75 date_from: None,
76 date_to: None,
77 limit: Some(50),
78 offset: None,
79 is_filters: Vec::new(),
80 priority: None,
81 tags_include: Vec::new(),
82 tags_exclude: Vec::new(),
83 }
84 }
85
86 /// Filters results to specific types.
87 pub fn with_types(mut self, types: Vec<SearchResultType>) -> Self {
88 self.types = Some(types);
89 self
90 }
91
92 /// Filters results to a specific project by ID.
93 pub fn with_project(mut self, project_id: ProjectId) -> Self {
94 self.project_id = Some(project_id);
95 self
96 }
97
98 /// Filters results to a specific project by name.
99 pub fn with_project_name(mut self, name: impl Into<String>) -> Self {
100 self.project_name = Some(name.into());
101 self
102 }
103
104 /// Sets the maximum number of results. Negative values are clamped to 0:
105 /// SQLite treats a negative LIMIT as unbounded, so an unclamped negative
106 /// would silently disable the cap instead of enforcing it.
107 pub fn with_limit(mut self, limit: i64) -> Self {
108 self.limit = Some(limit.max(0));
109 self
110 }
111
112 /// Sets the pagination offset. Negative values are clamped to 0.
113 pub fn with_offset(mut self, offset: i64) -> Self {
114 self.offset = Some(offset.max(0));
115 self
116 }
117
118 /// Filters results to items on or after this date.
119 pub fn with_date_from(mut self, date: DateTime<Utc>) -> Self {
120 self.date_from = Some(date);
121 self
122 }
123
124 /// Filters results to items on or before this date.
125 pub fn with_date_to(mut self, date: DateTime<Utc>) -> Self {
126 self.date_to = Some(date);
127 self
128 }
129
130 /// Adds `is:` filters.
131 pub fn with_is_filters(mut self, filters: Vec<crate::search_parser::IsFilter>) -> Self {
132 self.is_filters = filters;
133 self
134 }
135
136 /// Sets priority filter.
137 pub fn with_priority(mut self, priority: crate::models::Priority) -> Self {
138 self.priority = Some(priority);
139 self
140 }
141
142 /// Sets tags to include.
143 pub fn with_tags_include(mut self, tags: Vec<String>) -> Self {
144 self.tags_include = tags;
145 self
146 }
147
148 /// Sets tags to exclude.
149 pub fn with_tags_exclude(mut self, tags: Vec<String>) -> Self {
150 self.tags_exclude = tags;
151 self
152 }
153 }
154
155 /// Repository for full-text search across all content types.
156 #[async_trait]
157 pub trait SearchRepository: Send + Sync {
158 /// Searches across all indexed content using FTS.
159 /// Returns (results, total_count) where total_count is the pre-pagination count.
160 async fn search(&self, user_id: UserId, query: SearchQuery) -> Result<(Vec<SearchResultItem>, usize)>;
161 }
162