firestore_db_and_auth/documents/query.rs
1use super::*;
2use std::vec::IntoIter;
3
4///
5/// Queries the database for specific documents, for example all documents in a collection of 'type' == "car".
6///
7/// Example:
8/// ```no_run
9/// # use serde::{Serialize, Deserialize};
10/// #[derive(Debug, Serialize, Deserialize)]
11/// struct DemoDTO { a_string: String, an_int: u32, }
12///
13/// use firestore_db_and_auth::{documents, dto};
14/// # use firestore_db_and_auth::{credentials::Credentials, ServiceSession, errors::Result};
15/// # use firestore_db_and_auth::credentials::doctest_credentials;
16/// # tokio_test::block_on(async {
17/// # let session = ServiceSession::new(doctest_credentials().await).await.unwrap();
18///
19/// let values: documents::Query = documents::query(&session, "tests", "Sam Weiss".into(), dto::FieldOperator::EQUAL, "id").await.unwrap();
20/// for metadata in values {
21/// println!("id: {}, created: {}, updated: {}", &metadata.name, metadata.create_time.as_ref().unwrap(), metadata.update_time.as_ref().unwrap());
22/// // Fetch the actual document
23/// // The data is wrapped in a Result<> because fetching new data could have failed
24/// let doc : DemoDTO = documents::read_by_name(&session, &metadata.name).await.unwrap();
25/// println!("{:?}", doc);
26/// }
27/// # })
28/// ```
29///
30/// ## Arguments
31/// * 'auth' The authentication token
32/// * 'collectionid' The collection id; "my_collection" or "a/nested/collection"
33/// * 'value' The query / filter value. For example "car".
34/// * 'operator' The query operator. For example "EQUAL".
35/// * 'field' The query / filter field. For example "type".
36pub async fn query(
37 auth: &impl FirebaseAuthBearer,
38 collection_id: &str,
39 value: serde_json::Value,
40 operator: dto::FieldOperator,
41 field: &str,
42) -> Result<Query> {
43 let url = firebase_url_query(auth.project_id());
44 let value = crate::firebase_rest_to_rust::serde_value_to_firebase_value(&value);
45
46 let query_request = dto::RunQueryRequest {
47 structured_query: Some(dto::StructuredQuery {
48 select: Some(dto::Projection { fields: None }),
49 where_: Some(dto::Filter {
50 field_filter: Some(dto::FieldFilter {
51 value,
52 op: operator,
53 field: dto::FieldReference {
54 field_path: field.to_owned(),
55 },
56 }),
57 ..Default::default()
58 }),
59 from: Some(vec![dto::CollectionSelector {
60 collection_id: Some(collection_id.to_owned()),
61 ..Default::default()
62 }]),
63 ..Default::default()
64 }),
65 ..Default::default()
66 };
67
68 let resp = auth
69 .client()
70 .post(&url)
71 .bearer_auth(auth.access_token().await)
72 .json(&query_request)
73 .send()
74 .await?;
75
76 let resp = extract_google_api_error_async(resp, || collection_id.to_owned()).await?;
77
78 let json: Option<Vec<dto::RunQueryResponse>> = resp.json().await?;
79
80 Ok(Query(json.unwrap_or_default().into_iter()))
81}
82
83/// This type is returned as a result by [`query`].
84/// Use it as an iterator. The query API returns a list of document references, not the documents itself.
85///
86/// If you just need the meta data like the document name or update time, you are already settled.
87/// To fetch the document itself, use [`read_by_name`].
88///
89/// Please note that this API acts as an iterator of same-like documents.
90/// This type is not suitable if you want to list documents of different types.
91#[derive(Debug)]
92pub struct Query(IntoIter<dto::RunQueryResponse>);
93
94impl Iterator for Query {
95 type Item = dto::Document;
96
97 // Skip empty entries
98 fn next(&mut self) -> Option<Self::Item> {
99 while let Some(r) = self.0.next() {
100 if let Some(document) = r.document {
101 return Some(document);
102 }
103 }
104 return None;
105 }
106}