shaperail_runtime/db/
search.rs1#[derive(Debug, Clone)]
7pub struct SearchParam {
8 pub term: String,
10 pub fields: Vec<String>,
12}
13
14impl SearchParam {
15 pub fn new(term: &str, fields: &[String]) -> Option<Self> {
17 let term = term.trim();
18 if term.is_empty() || fields.is_empty() {
19 return None;
20 }
21 Some(SearchParam {
22 term: term.to_string(),
23 fields: fields.to_vec(),
24 })
25 }
26
27 pub fn apply_to_sql(&self, sql: &mut String, has_where: bool, param_offset: usize) -> usize {
33 if has_where {
34 sql.push_str(" AND ");
35 } else {
36 sql.push_str(" WHERE ");
37 }
38
39 let tsvector_expr = self
41 .fields
42 .iter()
43 .enumerate()
44 .map(|(i, f)| {
45 if i == 0 {
46 format!("COALESCE(\"{f}\", '')")
47 } else {
48 format!(" || ' ' || COALESCE(\"{f}\", '')")
49 }
50 })
51 .collect::<String>();
52
53 sql.push_str(&format!(
54 "to_tsvector('english', {tsvector_expr}) @@ plainto_tsquery('english', ${param_offset})"
55 ));
56
57 param_offset + 1
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn search_param_new_valid() {
67 let fields = vec!["name".to_string(), "email".to_string()];
68 let sp = SearchParam::new("john", &fields);
69 assert!(sp.is_some());
70 let sp = sp.unwrap();
71 assert_eq!(sp.term, "john");
72 assert_eq!(sp.fields.len(), 2);
73 }
74
75 #[test]
76 fn search_param_new_empty_term() {
77 let fields = vec!["name".to_string()];
78 assert!(SearchParam::new("", &fields).is_none());
79 assert!(SearchParam::new(" ", &fields).is_none());
80 }
81
82 #[test]
83 fn search_param_new_empty_fields() {
84 assert!(SearchParam::new("john", &[]).is_none());
85 }
86
87 #[test]
88 fn apply_search_single_field() {
89 let sp = SearchParam {
90 term: "john".to_string(),
91 fields: vec!["name".to_string()],
92 };
93
94 let mut sql = "SELECT * FROM users".to_string();
95 let offset = sp.apply_to_sql(&mut sql, false, 1);
96
97 assert_eq!(
98 sql,
99 "SELECT * FROM users WHERE to_tsvector('english', COALESCE(\"name\", '')) @@ plainto_tsquery('english', $1)"
100 );
101 assert_eq!(offset, 2);
102 }
103
104 #[test]
105 fn apply_search_multiple_fields() {
106 let sp = SearchParam {
107 term: "john".to_string(),
108 fields: vec!["name".to_string(), "email".to_string()],
109 };
110
111 let mut sql = "SELECT * FROM users".to_string();
112 let offset = sp.apply_to_sql(&mut sql, false, 1);
113
114 assert_eq!(
115 sql,
116 "SELECT * FROM users WHERE to_tsvector('english', COALESCE(\"name\", '') || ' ' || COALESCE(\"email\", '')) @@ plainto_tsquery('english', $1)"
117 );
118 assert_eq!(offset, 2);
119 }
120
121 #[test]
122 fn apply_search_with_existing_where() {
123 let sp = SearchParam {
124 term: "john".to_string(),
125 fields: vec!["name".to_string()],
126 };
127
128 let mut sql = "SELECT * FROM users WHERE \"role\" = $1".to_string();
129 let offset = sp.apply_to_sql(&mut sql, true, 2);
130
131 assert!(sql.contains("AND to_tsvector"));
132 assert_eq!(offset, 3);
133 }
134}