diff --git a/examples/explain-query.rs b/examples/explain-query.rs new file mode 100644 index 0000000..a134ceb --- /dev/null +++ b/examples/explain-query.rs @@ -0,0 +1,98 @@ +use chrono::{DateTime, Utc}; +use firestore::*; +use futures::stream::BoxStream; +use futures::TryStreamExt; +use serde::{Deserialize, Serialize}; + +pub fn config_env_var(name: &str) -> Result { + std::env::var(name).map_err(|e| format!("{}: {}", name, e)) +} + +// Example structure to play with +#[derive(Debug, Clone, Deserialize, Serialize)] +struct MyTestStructure { + some_id: String, + some_string: String, + one_more_string: String, + some_num: u64, + created_at: DateTime, +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Logging with debug enabled + let subscriber = tracing_subscriber::fmt() + .with_env_filter("firestore=debug") + .finish(); + tracing::subscriber::set_global_default(subscriber)?; + + // Create an instance + let db = FirestoreDb::new(&config_env_var("PROJECT_ID")?).await?; + + const TEST_COLLECTION_NAME: &'static str = "test-query"; + + if db + .fluent() + .select() + .by_id_in(TEST_COLLECTION_NAME) + .one("test-0") + .await? + .is_none() + { + println!("Populating a test collection"); + let batch_writer = db.create_simple_batch_writer().await?; + let mut current_batch = batch_writer.new_batch(); + + for i in 0..500 { + let my_struct = MyTestStructure { + some_id: format!("test-{}", i), + some_string: "Test".to_string(), + one_more_string: "Test2".to_string(), + some_num: i, + created_at: Utc::now(), + }; + + // Let's insert some data + db.fluent() + .update() + .in_col(TEST_COLLECTION_NAME) + .document_id(&my_struct.some_id) + .object(&my_struct) + .add_to_batch(&mut current_batch)?; + } + current_batch.write().await?; + } + + println!("Explain querying for a test collection as a stream using Fluent API"); + + // Query as a stream our data + let object_stream: BoxStream>> = db + .fluent() + .select() + .fields( + paths!(MyTestStructure::{some_id, some_num, some_string, one_more_string, created_at}), + ) + .from(TEST_COLLECTION_NAME) + .filter(|q| { + q.for_all([ + q.field(path!(MyTestStructure::some_num)).is_not_null(), + q.field(path!(MyTestStructure::some_string)).eq("Test"), + Some("Test2") + .and_then(|value| q.field(path!(MyTestStructure::one_more_string)).eq(value)), + ]) + }) + .order_by([( + path!(MyTestStructure::some_num), + FirestoreQueryDirection::Descending, + )]) + .explain() + //.explain_options(FirestoreExplainOptions::new().with_analyze(true)) or with analyze + .obj() + .stream_query_with_metadata() + .await?; + + let as_vec: Vec> = object_stream.try_collect().await?; + println!("{:?}", as_vec); + + Ok(()) +} diff --git a/src/firestore_meta_doc.rs b/src/firestore_meta.rs similarity index 94% rename from src/firestore_meta_doc.rs rename to src/firestore_meta.rs index 3579c10..056a589 100644 --- a/src/firestore_meta_doc.rs +++ b/src/firestore_meta.rs @@ -48,7 +48,11 @@ impl TryFrom for FirestoreWithMetadata { Ok(FirestoreWithMetadata { document: value.document, metadata: FirestoreDocumentMetadata { - transaction_id: Some(value.transaction), + transaction_id: if !value.transaction.is_empty() { + Some(value.transaction) + } else { + None + }, read_time: value.read_time.map(from_timestamp).transpose()?, skipped_results: value.skipped_results as usize, explain_metrics: value.explain_metrics.map(|v| v.try_into()).transpose()?, diff --git a/src/fluent_api/select_builder.rs b/src/fluent_api/select_builder.rs index fc03b11..2112766 100644 --- a/src/fluent_api/select_builder.rs +++ b/src/fluent_api/select_builder.rs @@ -3,12 +3,12 @@ use crate::select_aggregation_builder::FirestoreAggregationBuilder; use crate::select_filter_builder::FirestoreQueryFilterBuilder; use crate::{ FirestoreAggregatedQueryParams, FirestoreAggregatedQuerySupport, FirestoreAggregation, - FirestoreCollectionDocuments, FirestoreGetByIdSupport, FirestoreListenSupport, - FirestoreListener, FirestoreListenerParams, FirestoreListenerTarget, + FirestoreCollectionDocuments, FirestoreExplainOptions, FirestoreGetByIdSupport, + FirestoreListenSupport, FirestoreListener, FirestoreListenerParams, FirestoreListenerTarget, FirestoreListenerTargetParams, FirestorePartition, FirestorePartitionQueryParams, FirestoreQueryCollection, FirestoreQueryCursor, FirestoreQueryFilter, FirestoreQueryOrder, FirestoreQueryParams, FirestoreQuerySupport, FirestoreResult, FirestoreResumeStateStorage, - FirestoreTargetType, + FirestoreTargetType, FirestoreWithMetadata, }; use futures::stream::BoxStream; use gcloud_sdk::google::firestore::v1::Document; @@ -189,6 +189,25 @@ where } } + pub fn explain(self) -> FirestoreSelectDocBuilder<'a, D> { + Self { + params: self + .params + .with_explain_options(FirestoreExplainOptions::new()), + ..self + } + } + + pub fn explain_options( + self, + options: FirestoreExplainOptions, + ) -> FirestoreSelectDocBuilder<'a, D> { + Self { + params: self.params.with_explain_options(options), + ..self + } + } + #[inline] pub fn obj(self) -> FirestoreSelectObjBuilder<'a, D, T> where @@ -238,6 +257,12 @@ where ) -> FirestoreResult>> { self.db.stream_query_doc_with_errors(self.params).await } + + pub async fn stream_query_with_metadata<'b>( + self, + ) -> FirestoreResult>>> { + self.db.stream_query_doc_with_metadata(self.params).await + } } #[derive(Clone, Debug)] @@ -289,6 +314,15 @@ where self.db.stream_query_obj_with_errors(self.params).await } + pub async fn stream_query_with_metadata<'b>( + self, + ) -> FirestoreResult>>> + where + T: 'b, + { + self.db.stream_query_obj_with_metadata(self.params).await + } + pub fn partition_query(self) -> FirestorePartitionQueryObjBuilder<'a, D, T> where T: 'a, diff --git a/src/lib.rs b/src/lib.rs index 6d7db74..6587776 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -150,9 +150,9 @@ pub type FirestoreResult = std::result::Result; pub type FirestoreDocument = gcloud_sdk::google::firestore::v1::Document; -mod firestore_meta_doc; +mod firestore_meta; -pub use firestore_meta_doc::*; +pub use firestore_meta::*; mod firestore_document_functions;