pubmed_client/pubmed/query/
advanced.rs

1//! Advanced search methods for MeSH terms, authors, and specialized filtering
2
3use super::SearchQuery;
4
5impl SearchQuery {
6    /// Filter by MeSH major topic
7    ///
8    /// # Arguments
9    ///
10    /// * `mesh_term` - MeSH term to filter by as a major topic
11    ///
12    /// # Example
13    ///
14    /// ```
15    /// use pubmed_client::pubmed::SearchQuery;
16    ///
17    /// let query = SearchQuery::new()
18    ///     .mesh_major_topic("Diabetes Mellitus, Type 2");
19    /// ```
20    pub fn mesh_major_topic<S: Into<String>>(mut self, mesh_term: S) -> Self {
21        self.filters.push(format!("{}[majr]", mesh_term.into()));
22        self
23    }
24
25    /// Filter by MeSH term
26    ///
27    /// # Arguments
28    ///
29    /// * `mesh_term` - MeSH term to filter by
30    ///
31    /// # Example
32    ///
33    /// ```
34    /// use pubmed_client::pubmed::SearchQuery;
35    ///
36    /// let query = SearchQuery::new()
37    ///     .mesh_term("Neoplasms");
38    /// ```
39    pub fn mesh_term<S: Into<String>>(mut self, mesh_term: S) -> Self {
40        self.filters.push(format!("{}[mh]", mesh_term.into()));
41        self
42    }
43
44    /// Filter by multiple MeSH terms
45    ///
46    /// # Arguments
47    ///
48    /// * `mesh_terms` - MeSH terms to filter by
49    ///
50    /// # Example
51    ///
52    /// ```
53    /// use pubmed_client::pubmed::SearchQuery;
54    ///
55    /// let query = SearchQuery::new()
56    ///     .mesh_terms(&["Neoplasms", "Antineoplastic Agents"]);
57    /// ```
58    pub fn mesh_terms<S: AsRef<str>>(mut self, mesh_terms: &[S]) -> Self {
59        for term in mesh_terms {
60            self = self.mesh_term(term.as_ref());
61        }
62        self
63    }
64
65    /// Filter by MeSH subheading
66    ///
67    /// # Arguments
68    ///
69    /// * `subheading` - MeSH subheading to filter by
70    ///
71    /// # Example
72    ///
73    /// ```
74    /// use pubmed_client::pubmed::SearchQuery;
75    ///
76    /// let query = SearchQuery::new()
77    ///     .mesh_term("Diabetes Mellitus")
78    ///     .mesh_subheading("drug therapy");
79    /// ```
80    pub fn mesh_subheading<S: Into<String>>(mut self, subheading: S) -> Self {
81        self.filters.push(format!("{}[sh]", subheading.into()));
82        self
83    }
84
85    /// Filter by first author
86    ///
87    /// # Arguments
88    ///
89    /// * `author` - First author name to search for
90    ///
91    /// # Example
92    ///
93    /// ```
94    /// use pubmed_client::pubmed::SearchQuery;
95    ///
96    /// let query = SearchQuery::new()
97    ///     .query("cancer treatment")
98    ///     .first_author("Smith J");
99    /// ```
100    pub fn first_author<S: Into<String>>(mut self, author: S) -> Self {
101        self.filters.push(format!("{}[1au]", author.into()));
102        self
103    }
104
105    /// Filter by last author
106    ///
107    /// # Arguments
108    ///
109    /// * `author` - Last author name to search for
110    ///
111    /// # Example
112    ///
113    /// ```
114    /// use pubmed_client::pubmed::SearchQuery;
115    ///
116    /// let query = SearchQuery::new()
117    ///     .query("genomics")
118    ///     .last_author("Johnson M");
119    /// ```
120    pub fn last_author<S: Into<String>>(mut self, author: S) -> Self {
121        self.filters.push(format!("{}[lastau]", author.into()));
122        self
123    }
124
125    /// Filter by any author
126    ///
127    /// # Arguments
128    ///
129    /// * `author` - Author name to search for
130    ///
131    /// # Example
132    ///
133    /// ```
134    /// use pubmed_client::pubmed::SearchQuery;
135    ///
136    /// let query = SearchQuery::new()
137    ///     .query("machine learning")
138    ///     .author("Williams K");
139    /// ```
140    pub fn author<S: Into<String>>(mut self, author: S) -> Self {
141        self.filters.push(format!("{}[au]", author.into()));
142        self
143    }
144
145    /// Filter by institution/affiliation
146    ///
147    /// # Arguments
148    ///
149    /// * `institution` - Institution name to search for
150    ///
151    /// # Example
152    ///
153    /// ```
154    /// use pubmed_client::pubmed::SearchQuery;
155    ///
156    /// let query = SearchQuery::new()
157    ///     .query("cardiology research")
158    ///     .affiliation("Harvard Medical School");
159    /// ```
160    pub fn affiliation<S: Into<String>>(mut self, institution: S) -> Self {
161        self.filters.push(format!("{}[ad]", institution.into()));
162        self
163    }
164
165    /// Filter by ORCID identifier
166    ///
167    /// # Arguments
168    ///
169    /// * `orcid_id` - ORCID identifier to search for
170    ///
171    /// # Example
172    ///
173    /// ```
174    /// use pubmed_client::pubmed::SearchQuery;
175    ///
176    /// let query = SearchQuery::new()
177    ///     .query("computational biology")
178    ///     .orcid("0000-0001-2345-6789");
179    /// ```
180    pub fn orcid<S: Into<String>>(mut self, orcid_id: S) -> Self {
181        self.filters.push(format!("{}[auid]", orcid_id.into()));
182        self
183    }
184
185    /// Filter by organism using MeSH terms (scientific or common name)
186    ///
187    /// # Arguments
188    ///
189    /// * `organism` - Organism name (scientific or common name)
190    ///
191    /// # Examples
192    ///
193    /// ```
194    /// use pubmed_client::pubmed::SearchQuery;
195    ///
196    /// // Using scientific name
197    /// let query = SearchQuery::new()
198    ///     .query("gene expression")
199    ///     .organism_mesh("Mus musculus");
200    ///
201    /// // Using common name
202    /// let query = SearchQuery::new()
203    ///     .query("metabolism")
204    ///     .organism_mesh("Mice");
205    ///
206    /// // Using bacteria
207    /// let query = SearchQuery::new()
208    ///     .query("antibiotic resistance")
209    ///     .organism_mesh("Escherichia coli");
210    /// ```
211    pub fn organism_mesh<S: Into<String>>(mut self, organism: S) -> Self {
212        self.filters.push(format!("{}[mh]", organism.into()));
213        self
214    }
215
216    /// Filter to human studies only
217    ///
218    /// # Example
219    ///
220    /// ```
221    /// use pubmed_client::pubmed::SearchQuery;
222    ///
223    /// let query = SearchQuery::new()
224    ///     .query("drug treatment")
225    ///     .human_studies_only();
226    /// ```
227    pub fn human_studies_only(mut self) -> Self {
228        self.filters.push("humans[mh]".to_string());
229        self
230    }
231
232    /// Filter to animal studies only
233    ///
234    /// # Example
235    ///
236    /// ```
237    /// use pubmed_client::pubmed::SearchQuery;
238    ///
239    /// let query = SearchQuery::new()
240    ///     .query("preclinical research")
241    ///     .animal_studies_only();
242    /// ```
243    pub fn animal_studies_only(mut self) -> Self {
244        self.filters.push("animals[mh]".to_string());
245        self
246    }
247
248    /// Filter by age group
249    ///
250    /// # Arguments
251    ///
252    /// * `age_group` - Age group to filter by (e.g., "Child", "Adult", "Aged")
253    ///
254    /// # Example
255    ///
256    /// ```
257    /// use pubmed_client::pubmed::SearchQuery;
258    ///
259    /// let query = SearchQuery::new()
260    ///     .query("pediatric medicine")
261    ///     .age_group("Child");
262    /// ```
263    pub fn age_group<S: Into<String>>(mut self, age_group: S) -> Self {
264        self.filters.push(format!("{}[mh]", age_group.into()));
265        self
266    }
267
268    /// Add a custom filter
269    ///
270    /// # Arguments
271    ///
272    /// * `filter` - Custom filter string in PubMed syntax
273    ///
274    /// # Example
275    ///
276    /// ```
277    /// use pubmed_client::pubmed::SearchQuery;
278    ///
279    /// let query = SearchQuery::new()
280    ///     .query("research")
281    ///     .custom_filter("humans[mh]");
282    /// ```
283    pub fn custom_filter<S: Into<String>>(mut self, filter: S) -> Self {
284        self.filters.push(filter.into());
285        self
286    }
287}
288
289#[cfg(test)]
290mod tests {
291    use super::*;
292
293    #[test]
294    fn test_mesh_term() {
295        let query = SearchQuery::new().mesh_term("Neoplasms");
296        assert_eq!(query.build(), "Neoplasms[mh]");
297    }
298
299    #[test]
300    fn test_mesh_major_topic() {
301        let query = SearchQuery::new().mesh_major_topic("Diabetes Mellitus, Type 2");
302        assert_eq!(query.build(), "Diabetes Mellitus, Type 2[majr]");
303    }
304
305    #[test]
306    fn test_multiple_mesh_terms() {
307        let mesh_terms = ["Neoplasms", "Antineoplastic Agents"];
308        let query = SearchQuery::new().mesh_terms(&mesh_terms);
309        assert_eq!(query.build(), "Neoplasms[mh] AND Antineoplastic Agents[mh]");
310    }
311
312    #[test]
313    fn test_mesh_subheading() {
314        let query = SearchQuery::new()
315            .mesh_term("Diabetes Mellitus")
316            .mesh_subheading("drug therapy");
317        assert_eq!(query.build(), "Diabetes Mellitus[mh] AND drug therapy[sh]");
318    }
319
320    #[test]
321    fn test_first_author() {
322        let query = SearchQuery::new().first_author("Smith J");
323        assert_eq!(query.build(), "Smith J[1au]");
324    }
325
326    #[test]
327    fn test_last_author() {
328        let query = SearchQuery::new().last_author("Johnson M");
329        assert_eq!(query.build(), "Johnson M[lastau]");
330    }
331
332    #[test]
333    fn test_any_author() {
334        let query = SearchQuery::new().author("Williams K");
335        assert_eq!(query.build(), "Williams K[au]");
336    }
337
338    #[test]
339    fn test_affiliation() {
340        let query = SearchQuery::new().affiliation("Harvard Medical School");
341        assert_eq!(query.build(), "Harvard Medical School[ad]");
342    }
343
344    #[test]
345    fn test_orcid() {
346        let query = SearchQuery::new().orcid("0000-0001-2345-6789");
347        assert_eq!(query.build(), "0000-0001-2345-6789[auid]");
348    }
349
350    #[test]
351    fn test_organism_mesh() {
352        let query = SearchQuery::new().organism_mesh("Mus musculus");
353        assert_eq!(query.build(), "Mus musculus[mh]");
354    }
355
356    #[test]
357    fn test_organism_mesh_with_common_name() {
358        let query = SearchQuery::new().organism_mesh("Mice");
359        assert_eq!(query.build(), "Mice[mh]");
360    }
361
362    #[test]
363    fn test_human_studies_only() {
364        let query = SearchQuery::new().human_studies_only();
365        assert_eq!(query.build(), "humans[mh]");
366    }
367
368    #[test]
369    fn test_animal_studies_only() {
370        let query = SearchQuery::new().animal_studies_only();
371        assert_eq!(query.build(), "animals[mh]");
372    }
373
374    #[test]
375    fn test_age_group() {
376        let query = SearchQuery::new().age_group("Child");
377        assert_eq!(query.build(), "Child[mh]");
378    }
379
380    #[test]
381    fn test_custom_filter() {
382        let query = SearchQuery::new().custom_filter("custom[field]");
383        assert_eq!(query.build(), "custom[field]");
384    }
385
386    #[test]
387    fn test_combined_advanced_filters() {
388        let query = SearchQuery::new()
389            .query("cancer treatment")
390            .mesh_term("Neoplasms")
391            .author("Smith J")
392            .human_studies_only()
393            .affiliation("Harvard");
394
395        let expected =
396            "cancer treatment AND Neoplasms[mh] AND Smith J[au] AND humans[mh] AND Harvard[ad]";
397        assert_eq!(query.build(), expected);
398    }
399
400    #[test]
401    fn test_empty_mesh_terms_array() {
402        let mesh_terms: &[&str] = &[];
403        let query = SearchQuery::new().mesh_terms(mesh_terms);
404        assert_eq!(query.build(), "");
405    }
406
407    #[test]
408    fn test_single_mesh_term_via_array() {
409        let mesh_terms = ["Neoplasms"];
410        let query = SearchQuery::new().mesh_terms(&mesh_terms);
411        assert_eq!(query.build(), "Neoplasms[mh]");
412    }
413
414    #[test]
415    fn test_mesh_term_with_spaces() {
416        let query = SearchQuery::new().mesh_term("Diabetes Mellitus, Type 2");
417        assert_eq!(query.build(), "Diabetes Mellitus, Type 2[mh]");
418    }
419
420    #[test]
421    fn test_author_with_special_characters() {
422        let query = SearchQuery::new().author("O'Connor J");
423        assert_eq!(query.build(), "O'Connor J[au]");
424    }
425
426    #[test]
427    fn test_affiliation_with_special_characters() {
428        let query = SearchQuery::new().affiliation("Johns Hopkins & MIT");
429        assert_eq!(query.build(), "Johns Hopkins & MIT[ad]");
430    }
431
432    #[test]
433    fn test_custom_filter_preservation() {
434        let query = SearchQuery::new()
435            .custom_filter("first[custom]")
436            .custom_filter("second[custom]");
437        assert_eq!(query.build(), "first[custom] AND second[custom]");
438    }
439}