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