Skip to main content

paperless_api/
document_query.rs

1//! Query for documents using the [`DocumentQueryBuilder`].
2
3use crate::{
4    document::ArchiveSerialNumber,
5    id::{CorrespondentId, TagId},
6};
7
8/// Builder for constructing document queries.
9#[derive(Default)]
10pub struct DocumentQueryBuilder {
11    archive_serial_number: Option<ArchiveSerialNumber>,
12    correspondent_id_in: Option<Vec<CorrespondentId>>,
13    correspondent_name_icontains: Option<String>,
14    content_icontains: Option<String>,
15    title_icontains: Option<String>,
16    tags_id_in: Option<Vec<TagId>>,
17    pub(crate) full_content: bool,
18    full_permissions: bool,
19}
20
21/// A constructed document query.
22pub struct DocumentQuery {
23    pub(crate) query: Vec<(&'static str, String)>,
24}
25
26pub(crate) const QUERY_PARAM_FULL_PERMISSIONS: &str = "full_perms";
27pub(crate) const QUERY_PARAM_TRUNCATE_CONTENT: &str = "truncate_content";
28const QUERY_PARAM_TAGS_ID_IN: &str = "tags__id__in";
29const QUERY_PARAM_ARCHIVE_SERIAL_NUMBER: &str = "archive_serial_number";
30const QUERY_PARAM_CORRESPONDENT_ID_IN: &str = "correspondent__id__in";
31const QUERY_PARAM_CORRESPONDENT_NAME_ICONTAINS: &str = "correspondent__name__icontains";
32const QUERY_PARAM_CONTENT_ICONTAINS: &str = "content__icontains";
33const QUERY_PARAM_TITLE_ICONTAINS: &str = "title__icontains";
34
35impl DocumentQueryBuilder {
36    /// Filters documents which have the given archive serial number.
37    #[must_use]
38    pub fn archive_serial_number(mut self, archive_serial_number: ArchiveSerialNumber) -> Self {
39        self.archive_serial_number = Some(archive_serial_number);
40        self
41    }
42
43    /// Filters documents which have any of the given correspondents.
44    #[must_use]
45    pub fn correspondent_id_in(mut self, correspondent_id_in: Vec<CorrespondentId>) -> Self {
46        self.correspondent_id_in = Some(correspondent_id_in);
47        self
48    }
49
50    /// Filters documents which have a correspondent name containing the given string.
51    #[must_use]
52    pub fn correspondent_name_icontains(mut self, correspondent_name_icontains: String) -> Self {
53        self.correspondent_name_icontains = Some(correspondent_name_icontains);
54        self
55    }
56
57    /// Filters documents which have content containing the given string.
58    #[must_use]
59    pub fn content_icontains(mut self, content_icontains: String) -> Self {
60        self.content_icontains = Some(content_icontains);
61        self
62    }
63
64    /// Filters documents which have a title containing the given string.
65    #[must_use]
66    pub fn title_icontains(mut self, title_icontains: String) -> Self {
67        self.title_icontains = Some(title_icontains);
68        self
69    }
70
71    /// Filters documents which have any of the given tags.
72    #[must_use]
73    pub fn tags_id_in(mut self, tags_id_in: Vec<TagId>) -> Self {
74        self.tags_id_in = Some(tags_id_in);
75        self
76    }
77
78    /// Returns documents with full content (truncated by default to save bandwidth).
79    #[must_use]
80    pub fn full_content(mut self, full_content: bool) -> Self {
81        self.full_content = full_content;
82        self
83    }
84
85    /// Returns documents with full permissions data.
86    #[must_use]
87    pub fn full_permissions(mut self, full_permissions: bool) -> Self {
88        self.full_permissions = full_permissions;
89        self
90    }
91
92    /// Builds the query.
93    #[must_use]
94    pub fn build(self) -> DocumentQuery {
95        let mut query = vec![];
96
97        if let Some(archive_serial_number) = self.archive_serial_number {
98            query.push((
99                QUERY_PARAM_ARCHIVE_SERIAL_NUMBER,
100                archive_serial_number.0.to_string(),
101            ));
102        }
103
104        if let Some(correspondent_id_in) = self.correspondent_id_in {
105            query.push((
106                QUERY_PARAM_CORRESPONDENT_ID_IN,
107                correspondent_id_in
108                    .iter()
109                    .map(|id| id.0.to_string())
110                    .collect::<Vec<_>>()
111                    .join(","),
112            ));
113        }
114
115        if let Some(correspondent_name_icontains) = self.correspondent_name_icontains {
116            query.push((
117                QUERY_PARAM_CORRESPONDENT_NAME_ICONTAINS,
118                correspondent_name_icontains,
119            ));
120        }
121
122        if let Some(content_icontains) = self.content_icontains {
123            query.push((QUERY_PARAM_CONTENT_ICONTAINS, content_icontains));
124        }
125
126        if let Some(title_icontains) = self.title_icontains {
127            query.push((QUERY_PARAM_TITLE_ICONTAINS, title_icontains));
128        }
129
130        if let Some(tags_id_in) = self.tags_id_in {
131            query.push((
132                QUERY_PARAM_TAGS_ID_IN,
133                tags_id_in
134                    .iter()
135                    .map(|id| id.0.to_string())
136                    .collect::<Vec<_>>()
137                    .join(","),
138            ));
139        }
140
141        if !self.full_content {
142            query.push((QUERY_PARAM_TRUNCATE_CONTENT, "true".to_string()));
143        }
144
145        if self.full_permissions {
146            query.push((QUERY_PARAM_FULL_PERMISSIONS, "true".to_string()));
147        }
148
149        DocumentQuery { query }
150    }
151}