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