1use reqwest::StatusCode;
20
21use serde::{Deserialize, Serialize};
22
23use crate::{
24 error::EsError,
25 json::ShouldSkip,
26 operations::{
27 common::{OptionVal, Options},
28 format_indexes_and_types, ShardCountResult,
29 },
30 query::Query,
31 Client, EsResponse,
32};
33
34#[derive(Debug)]
36pub struct CountURIOperation<'a, 'b> {
37 client: &'a mut Client,
38 indexes: &'b [&'b str],
39 doc_types: &'b [&'b str],
40 options: Options<'b>,
41}
42
43impl<'a, 'b> CountURIOperation<'a, 'b> {
44 pub fn new(client: &'a mut Client) -> CountURIOperation<'a, 'b> {
45 CountURIOperation {
46 client,
47 indexes: &[],
48 doc_types: &[],
49 options: Options::default(),
50 }
51 }
52
53 pub fn with_indexes(&'b mut self, indexes: &'b [&'b str]) -> &'b mut Self {
54 self.indexes = indexes;
55 self
56 }
57
58 pub fn with_types(&'b mut self, doc_types: &'b [&'b str]) -> &'b mut Self {
59 self.doc_types = doc_types;
60 self
61 }
62
63 pub fn with_query<S: Into<String>>(&'b mut self, qs: S) -> &'b mut Self {
64 self.options.push("q", qs.into());
65 self
66 }
67
68 add_option!(with_df, "df");
69 add_option!(with_analyzer, "analyzer");
70 add_option!(with_default_operator, "default_operator");
71 add_option!(with_lenient, "lenient");
72 add_option!(with_analyze_wildcard, "analyze_wildcard");
73 add_option!(with_terminate_after, "terminate_after");
74
75 pub fn send(&'b mut self) -> Result<CountResult, EsError> {
76 let url = format!(
77 "/{}/_count{}",
78 format_indexes_and_types(&self.indexes, &self.doc_types),
79 self.options
80 );
81 log::info!("Counting with: {}", url);
82 let response = self.client.get_op(&url)?;
83 match response.status_code() {
84 StatusCode::OK => Ok(response.read_response()?),
85 status_code => Err(EsError::EsError(format!(
86 "Unexpected status: {}",
87 status_code
88 ))),
89 }
90 }
91}
92
93#[derive(Debug, Default, Serialize)]
94struct CountQueryOperationBody<'b> {
95 #[serde(skip_serializing_if = "ShouldSkip::should_skip")]
97 query: Option<&'b Query>,
98}
99
100#[derive(Debug)]
101pub struct CountQueryOperation<'a, 'b> {
102 client: &'a mut Client,
104
105 indexes: &'b [&'b str],
107
108 doc_types: &'b [&'b str],
110
111 options: Options<'b>,
113
114 body: CountQueryOperationBody<'b>,
116}
117
118impl<'a, 'b> CountQueryOperation<'a, 'b> {
119 pub fn new(client: &'a mut Client) -> Self {
120 CountQueryOperation {
121 client,
122 indexes: &[],
123 doc_types: &[],
124 options: Options::new(),
125 body: Default::default(),
126 }
127 }
128
129 pub fn with_indexes(&'b mut self, indexes: &'b [&'b str]) -> &'b mut Self {
130 self.indexes = indexes;
131 self
132 }
133
134 pub fn with_types(&'b mut self, doc_types: &'b [&'b str]) -> &'b mut Self {
135 self.doc_types = doc_types;
136 self
137 }
138
139 pub fn with_query(&'b mut self, query: &'b Query) -> &'b mut Self {
140 self.body.query = Some(query);
141 self
142 }
143
144 add_option!(with_df, "df");
145 add_option!(with_analyzer, "analyzer");
146 add_option!(with_default_operator, "default_operator");
147 add_option!(with_lenient, "lenient");
148 add_option!(with_analyze_wildcard, "analyze_wildcard");
149 add_option!(with_terminate_after, "terminate_after");
150
151 pub fn send(&'b mut self) -> Result<CountResult, EsError> {
153 let url = format!(
154 "/{}/_count{}",
155 format_indexes_and_types(&self.indexes, &self.doc_types),
156 self.options
157 );
158 let response = self.client.post_body_op(&url, &self.body)?;
159 match response.status_code() {
160 StatusCode::OK => Ok(response.read_response()?),
161 status_code => Err(EsError::EsError(format!(
162 "Unexpected status: {}",
163 status_code
164 ))),
165 }
166 }
167}
168
169impl Client {
170 pub fn count_uri(&mut self) -> CountURIOperation {
174 CountURIOperation::new(self)
175 }
176
177 pub fn count_query(&mut self) -> CountQueryOperation {
181 CountQueryOperation::new(self)
182 }
183}
184
185#[derive(Debug, Deserialize)]
186pub struct CountResult {
187 pub count: u64,
188
189 #[serde(rename = "_shards")]
190 pub shards: ShardCountResult,
191}
192
193#[cfg(test)]
194mod tests {
195 use crate::tests::setup_test_data;
196 use crate::tests::{clean_db, make_client};
197
198 use crate::query::Query;
199
200 use super::CountResult;
201
202 #[test]
203 fn test_count_uri() {
204 let index_name = "test_count_uri";
205 let mut client = make_client();
206
207 clean_db(&mut client, index_name);
208 setup_test_data(&mut client, index_name);
209
210 let all_results: CountResult = client
211 .count_uri()
212 .with_indexes(&[index_name])
213 .send()
214 .unwrap();
215 assert_eq!(3, all_results.count);
216
217 let doc_1: CountResult = client
218 .count_uri()
219 .with_indexes(&[index_name])
220 .with_query("str_field:1ABC")
221 .send()
222 .unwrap();
223 assert_eq!(1, doc_1.count);
224
225 let not_found_doc: CountResult = client
226 .count_uri()
227 .with_indexes(&[index_name])
228 .with_query("str_field:lolol")
229 .send()
230 .unwrap();
231 assert_eq!(0, not_found_doc.count);
232 }
233
234 #[test]
235 fn test_count_query() {
236 let index_name = "test_count_query";
237 let mut client = make_client();
238
239 clean_db(&mut client, index_name);
240 setup_test_data(&mut client, index_name);
241
242 let all_results: CountResult = client
243 .count_query()
244 .with_indexes(&[index_name])
245 .with_query(&Query::build_match_all().build())
246 .send()
247 .unwrap();
248 assert_eq!(3, all_results.count);
249
250 let doc_1: CountResult = client
251 .count_query()
252 .with_indexes(&[index_name])
253 .with_query(
254 &Query::build_range("int_field")
255 .with_gte(2)
256 .with_lte(3)
257 .build(),
258 )
259 .send()
260 .unwrap();
261 assert_eq!(2, doc_1.count);
262
263 let not_found_doc: CountResult = client
264 .count_query()
265 .with_indexes(&[index_name])
266 .with_query(&Query::build_range("int_field").with_gte(99).build())
267 .send()
268 .unwrap();
269 assert_eq!(0, not_found_doc.count);
270 }
271}