systemprompt_content/services/search/
mod.rs1use crate::error::ContentError;
11use crate::models::{SearchRequest, SearchResponse, SearchResult};
12use crate::repository::{ContentRepository, SearchRepository};
13use systemprompt_identifiers::CategoryId;
14
15const DEFAULT_SEARCH_LIMIT: i64 = 10;
16
17#[derive(Debug)]
18pub struct SearchService {
19 search_repo: SearchRepository,
20 content_repo: ContentRepository,
21}
22
23impl SearchService {
24 pub const fn new(search_repo: SearchRepository, content_repo: ContentRepository) -> Self {
25 Self {
26 search_repo,
27 content_repo,
28 }
29 }
30
31 pub async fn search(&self, request: &SearchRequest) -> Result<SearchResponse, ContentError> {
32 let limit = request.limit.unwrap_or(DEFAULT_SEARCH_LIMIT);
33
34 let results = if let Some(filters) = &request.filters {
35 if let Some(category_id) = &filters.category_id {
36 self.search_repo
37 .search_by_category(category_id, limit)
38 .await?
39 } else {
40 vec![]
41 }
42 } else {
43 let content_list = self.content_repo.list_all(limit, 0).await?;
44 content_list
45 .into_iter()
46 .map(Self::content_to_search_result)
47 .collect()
48 };
49
50 Ok(SearchResponse {
51 total: results.len(),
52 results,
53 })
54 }
55
56 pub async fn search_by_category(
57 &self,
58 category_id: &CategoryId,
59 limit: i64,
60 ) -> Result<Vec<SearchResult>, ContentError> {
61 Ok(self
62 .search_repo
63 .search_by_category(category_id, limit)
64 .await?)
65 }
66
67 fn content_to_search_result(content: crate::models::Content) -> SearchResult {
68 SearchResult {
69 id: content.id,
70 title: content.title,
71 slug: content.slug,
72 description: content.description,
73 image: content.image,
74 view_count: 0,
75 source_id: content.source_id,
76 category_id: content.category_id,
77 }
78 }
79}