pubmed_client/pubmed/query/
boolean.rs1use super::SearchQuery;
4
5impl SearchQuery {
6 pub fn and(mut self, other: SearchQuery) -> Self {
22 let self_query = self.build();
24 let other_query = other.build();
25
26 if !self_query.is_empty() && !other_query.is_empty() {
27 let combined_query = format!("({self_query}) AND ({other_query})");
29 self.terms = vec![combined_query];
30 self.filters = Vec::new();
31 } else if !other_query.is_empty() {
32 self.terms = vec![other_query];
33 self.filters = Vec::new();
34 }
35
36 if other.limit.is_some() && (self.limit.is_none() || other.limit > self.limit) {
38 self.limit = other.limit;
39 }
40
41 self
42 }
43
44 pub fn or(mut self, other: SearchQuery) -> Self {
60 let self_query = self.build();
62 let other_query = other.build();
63
64 if !self_query.is_empty() && !other_query.is_empty() {
65 let combined_query = format!("({self_query}) OR ({other_query})");
67 self.terms = vec![combined_query];
68 self.filters = Vec::new();
69 } else if !other_query.is_empty() {
70 self.terms = vec![other_query];
71 self.filters = Vec::new();
72 }
73
74 if other.limit.is_some() && (self.limit.is_none() || other.limit > self.limit) {
76 self.limit = other.limit;
77 }
78
79 self
80 }
81
82 pub fn negate(mut self) -> Self {
94 let self_query = self.build();
95
96 if !self_query.is_empty() {
97 let negated_query = format!("NOT ({self_query})");
98 self.terms = vec![negated_query];
99 self.filters = Vec::new();
100 }
101
102 self
103 }
104
105 pub fn exclude(mut self, excluded: SearchQuery) -> Self {
121 let self_query = self.build();
122 let excluded_query = excluded.build();
123
124 if !self_query.is_empty() && !excluded_query.is_empty() {
125 let combined_query = format!("({self_query}) NOT ({excluded_query})");
126 self.terms = vec![combined_query];
127 self.filters = Vec::new();
128 }
129
130 self
131 }
132
133 pub fn group(mut self) -> Self {
146 let self_query = self.build();
147
148 if !self_query.is_empty() {
149 let grouped_query = format!("({self_query})");
150 self.terms = vec![grouped_query];
151 self.filters = Vec::new();
152 }
153
154 self
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn test_and_operation() {
164 let query1 = SearchQuery::new().query("covid-19");
165 let query2 = SearchQuery::new().query("vaccine");
166 let combined = query1.and(query2);
167 assert_eq!(combined.build(), "(covid-19) AND (vaccine)");
168 }
169
170 #[test]
171 fn test_or_operation() {
172 let query1 = SearchQuery::new().query("diabetes");
173 let query2 = SearchQuery::new().query("hypertension");
174 let combined = query1.or(query2);
175 assert_eq!(combined.build(), "(diabetes) OR (hypertension)");
176 }
177
178 #[test]
179 fn test_negate_operation() {
180 let query = SearchQuery::new().query("cancer").negate();
181 assert_eq!(query.build(), "NOT (cancer)");
182 }
183
184 #[test]
185 fn test_exclude_operation() {
186 let base_query = SearchQuery::new().query("cancer treatment");
187 let exclude_query = SearchQuery::new().query("animal studies");
188 let filtered = base_query.exclude(exclude_query);
189 assert_eq!(filtered.build(), "(cancer treatment) NOT (animal studies)");
190 }
191
192 #[test]
193 fn test_group_operation() {
194 let query = SearchQuery::new().query("cancer").group();
195 assert_eq!(query.build(), "(cancer)");
196 }
197
198 #[test]
199 fn test_complex_boolean_chain() {
200 let ai_query = SearchQuery::new().query("machine learning");
201 let medicine_query = SearchQuery::new().query("medicine");
202 let exclude_query = SearchQuery::new().query("veterinary");
203
204 let final_query = ai_query.and(medicine_query).exclude(exclude_query).group();
205
206 assert_eq!(
207 final_query.build(),
208 "(((machine learning) AND (medicine)) NOT (veterinary))"
209 );
210 }
211
212 #[test]
213 fn test_and_with_empty_queries() {
214 let query1 = SearchQuery::new();
215 let query2 = SearchQuery::new().query("test");
216 let combined = query1.and(query2);
217 assert_eq!(combined.build(), "test");
218 }
219
220 #[test]
221 fn test_or_with_empty_queries() {
222 let query1 = SearchQuery::new().query("test");
223 let query2 = SearchQuery::new();
224 let combined = query1.or(query2);
225 assert_eq!(combined.build(), "test");
226 }
227
228 #[test]
229 fn test_limit_preservation_in_boolean_ops() {
230 let query1 = SearchQuery::new().query("covid").limit(10);
231 let query2 = SearchQuery::new().query("vaccine").limit(50);
232 let combined = query1.and(query2);
233 assert_eq!(combined.get_limit(), 50); }
235
236 #[test]
237 fn test_negate_empty_query() {
238 let query = SearchQuery::new().negate();
239 assert_eq!(query.build(), "");
240 }
241
242 #[test]
243 fn test_exclude_empty_base() {
244 let base_query = SearchQuery::new();
245 let exclude_query = SearchQuery::new().query("test");
246 let filtered = base_query.exclude(exclude_query);
247 assert_eq!(filtered.build(), "");
248 }
249
250 #[test]
251 fn test_exclude_empty_excluded() {
252 let base_query = SearchQuery::new().query("test");
253 let exclude_query = SearchQuery::new();
254 let filtered = base_query.exclude(exclude_query);
255 assert_eq!(filtered.build(), "test");
256 }
257
258 #[test]
259 fn test_deep_boolean_nesting() {
260 let q1 = SearchQuery::new().query("a");
261 let q2 = SearchQuery::new().query("b");
262 let q3 = SearchQuery::new().query("c");
263 let q4 = SearchQuery::new().query("d");
264
265 let nested = q1.and(q2).or(q3.and(q4));
266 assert_eq!(nested.build(), "((a) AND (b)) OR ((c) AND (d))");
267 }
268
269 #[test]
270 fn test_group_empty_query() {
271 let query = SearchQuery::new().group();
272 assert_eq!(query.build(), "");
273 }
274}