1#![allow(clippy::derive_partial_eq_without_eq)]
4
5use crate::errors::{
6 FirestoreError, FirestoreInvalidParametersError, FirestoreInvalidParametersPublicDetails,
7};
8use crate::{FirestoreRequestOptions, FirestoreValue, FirestoreVector};
9use gcloud_sdk::google::firestore::v1::*;
10use rsb_derive::Builder;
11
12#[derive(Debug, Eq, PartialEq, Clone)]
14pub enum FirestoreQueryCollection {
15 Single(String),
17 Group(Vec<String>),
21}
22
23#[allow(clippy::to_string_trait_impl)]
24impl ToString for FirestoreQueryCollection {
25 fn to_string(&self) -> String {
26 match self {
27 FirestoreQueryCollection::Single(single) => single.to_string(),
28 FirestoreQueryCollection::Group(group) => group.join(","),
29 }
30 }
31}
32
33impl From<&str> for FirestoreQueryCollection {
34 fn from(collection_id_str: &str) -> Self {
35 FirestoreQueryCollection::Single(collection_id_str.to_string())
36 }
37}
38
39#[derive(Debug, PartialEq, Clone, Builder)]
45pub struct FirestoreQueryParams {
46 pub parent: Option<String>,
52
53 pub collection_id: FirestoreQueryCollection,
55
56 pub limit: Option<u32>,
58
59 pub offset: Option<u32>,
61
62 pub order_by: Option<Vec<FirestoreQueryOrder>>,
64
65 pub filter: Option<FirestoreQueryFilter>,
67
68 pub all_descendants: Option<bool>,
73
74 pub return_only_fields: Option<Vec<String>>,
77
78 pub start_at: Option<FirestoreQueryCursor>,
80
81 pub end_at: Option<FirestoreQueryCursor>,
83
84 pub explain_options: Option<FirestoreExplainOptions>,
86
87 pub find_nearest: Option<FirestoreFindNearestOptions>,
89
90 pub request_options: Option<FirestoreRequestOptions>,
95}
96
97impl TryFrom<FirestoreQueryParams> for StructuredQuery {
98 type Error = FirestoreError;
99
100 fn try_from(params: FirestoreQueryParams) -> Result<Self, Self::Error> {
101 let query_filter = params.filter.map(|f| f.into());
102
103 Ok(StructuredQuery {
104 select: params.return_only_fields.map(|select_only_fields| {
105 structured_query::Projection {
106 fields: select_only_fields
107 .into_iter()
108 .map(|field_name| structured_query::FieldReference {
109 field_path: field_name,
110 })
111 .collect(),
112 }
113 }),
114 start_at: params.start_at.map(|start_at| start_at.into()),
115 end_at: params.end_at.map(|end_at| end_at.into()),
116 limit: params.limit.map(|x| x as i32),
117 offset: params.offset.map(|x| x as i32).unwrap_or(0),
118 order_by: params
119 .order_by
120 .map(|po| po.into_iter().map(|fo| fo.into()).collect())
121 .unwrap_or_default(),
122 from: match params.collection_id {
123 FirestoreQueryCollection::Single(collection_id) => {
124 vec![structured_query::CollectionSelector {
125 collection_id,
126 all_descendants: params.all_descendants.unwrap_or(false),
127 }]
128 }
129 FirestoreQueryCollection::Group(collection_ids) => collection_ids
130 .into_iter()
131 .map(|collection_id| structured_query::CollectionSelector {
132 collection_id,
133 all_descendants: params.all_descendants.unwrap_or(false),
134 })
135 .collect(),
136 },
137 find_nearest: params
138 .find_nearest
139 .map(|find_nearest| find_nearest.try_into())
140 .transpose()?,
141 r#where: query_filter,
142 })
143 }
144}
145
146#[derive(Debug, PartialEq, Clone)]
151pub enum FirestoreQueryFilter {
152 Composite(FirestoreQueryFilterComposite),
154 Unary(FirestoreQueryFilterUnary),
156 Compare(Option<FirestoreQueryFilterCompare>),
160}
161
162impl From<FirestoreQueryFilter> for structured_query::Filter {
163 fn from(filter: FirestoreQueryFilter) -> Self {
164 let filter_type = match filter {
165 FirestoreQueryFilter::Compare(comp) => comp.map(|cmp| {
166 structured_query::filter::FilterType::FieldFilter(match cmp {
167 FirestoreQueryFilterCompare::Equal(field_name, fvalue) => {
168 structured_query::FieldFilter {
169 field: Some(structured_query::FieldReference {
170 field_path: field_name,
171 }),
172 op: structured_query::field_filter::Operator::Equal.into(),
173 value: Some(fvalue.value),
174 }
175 }
176 FirestoreQueryFilterCompare::NotEqual(field_name, fvalue) => {
177 structured_query::FieldFilter {
178 field: Some(structured_query::FieldReference {
179 field_path: field_name,
180 }),
181 op: structured_query::field_filter::Operator::NotEqual.into(),
182 value: Some(fvalue.value),
183 }
184 }
185 FirestoreQueryFilterCompare::In(field_name, fvalue) => {
186 structured_query::FieldFilter {
187 field: Some(structured_query::FieldReference {
188 field_path: field_name,
189 }),
190 op: structured_query::field_filter::Operator::In.into(),
191 value: Some(fvalue.value),
192 }
193 }
194 FirestoreQueryFilterCompare::NotIn(field_name, fvalue) => {
195 structured_query::FieldFilter {
196 field: Some(structured_query::FieldReference {
197 field_path: field_name,
198 }),
199 op: structured_query::field_filter::Operator::NotIn.into(),
200 value: Some(fvalue.value),
201 }
202 }
203 FirestoreQueryFilterCompare::ArrayContains(field_name, fvalue) => {
204 structured_query::FieldFilter {
205 field: Some(structured_query::FieldReference {
206 field_path: field_name,
207 }),
208 op: structured_query::field_filter::Operator::ArrayContains.into(),
209 value: Some(fvalue.value),
210 }
211 }
212 FirestoreQueryFilterCompare::ArrayContainsAny(field_name, fvalue) => {
213 structured_query::FieldFilter {
214 field: Some(structured_query::FieldReference {
215 field_path: field_name,
216 }),
217 op: structured_query::field_filter::Operator::ArrayContainsAny.into(),
218 value: Some(fvalue.value),
219 }
220 }
221 FirestoreQueryFilterCompare::LessThan(field_name, fvalue) => {
222 structured_query::FieldFilter {
223 field: Some(structured_query::FieldReference {
224 field_path: field_name,
225 }),
226 op: structured_query::field_filter::Operator::LessThan.into(),
227 value: Some(fvalue.value),
228 }
229 }
230 FirestoreQueryFilterCompare::LessThanOrEqual(field_name, fvalue) => {
231 structured_query::FieldFilter {
232 field: Some(structured_query::FieldReference {
233 field_path: field_name,
234 }),
235 op: structured_query::field_filter::Operator::LessThanOrEqual.into(),
236 value: Some(fvalue.value),
237 }
238 }
239 FirestoreQueryFilterCompare::GreaterThan(field_name, fvalue) => {
240 structured_query::FieldFilter {
241 field: Some(structured_query::FieldReference {
242 field_path: field_name,
243 }),
244 op: structured_query::field_filter::Operator::GreaterThan.into(),
245 value: Some(fvalue.value),
246 }
247 }
248 FirestoreQueryFilterCompare::GreaterThanOrEqual(field_name, fvalue) => {
249 structured_query::FieldFilter {
250 field: Some(structured_query::FieldReference {
251 field_path: field_name,
252 }),
253 op: structured_query::field_filter::Operator::GreaterThanOrEqual.into(),
254 value: Some(fvalue.value),
255 }
256 }
257 })
258 }),
259 FirestoreQueryFilter::Composite(composite) => {
260 Some(structured_query::filter::FilterType::CompositeFilter(
261 structured_query::CompositeFilter {
262 op: (Into::<structured_query::composite_filter::Operator>::into(
263 composite.operator,
264 ))
265 .into(),
266 filters: composite
267 .for_all_filters
268 .into_iter()
269 .map(structured_query::Filter::from)
270 .filter(|filter| filter.filter_type.is_some())
271 .collect(),
272 },
273 ))
274 }
275 FirestoreQueryFilter::Unary(unary) => match unary {
276 FirestoreQueryFilterUnary::IsNan(field_name) => {
277 Some(structured_query::filter::FilterType::UnaryFilter(
278 structured_query::UnaryFilter {
279 op: structured_query::unary_filter::Operator::IsNan.into(),
280 operand_type: Some(structured_query::unary_filter::OperandType::Field(
281 structured_query::FieldReference {
282 field_path: field_name,
283 },
284 )),
285 },
286 ))
287 }
288 FirestoreQueryFilterUnary::IsNull(field_name) => {
289 Some(structured_query::filter::FilterType::UnaryFilter(
290 structured_query::UnaryFilter {
291 op: structured_query::unary_filter::Operator::IsNull.into(),
292 operand_type: Some(structured_query::unary_filter::OperandType::Field(
293 structured_query::FieldReference {
294 field_path: field_name,
295 },
296 )),
297 },
298 ))
299 }
300 FirestoreQueryFilterUnary::IsNotNan(field_name) => {
301 Some(structured_query::filter::FilterType::UnaryFilter(
302 structured_query::UnaryFilter {
303 op: structured_query::unary_filter::Operator::IsNotNan.into(),
304 operand_type: Some(structured_query::unary_filter::OperandType::Field(
305 structured_query::FieldReference {
306 field_path: field_name,
307 },
308 )),
309 },
310 ))
311 }
312 FirestoreQueryFilterUnary::IsNotNull(field_name) => {
313 Some(structured_query::filter::FilterType::UnaryFilter(
314 structured_query::UnaryFilter {
315 op: structured_query::unary_filter::Operator::IsNotNull.into(),
316 operand_type: Some(structured_query::unary_filter::OperandType::Field(
317 structured_query::FieldReference {
318 field_path: field_name,
319 },
320 )),
321 },
322 ))
323 }
324 },
325 };
326
327 structured_query::Filter { filter_type }
328 }
329}
330
331#[derive(Debug, Eq, PartialEq, Clone, Builder)]
333pub struct FirestoreQueryOrder {
334 pub field_name: String,
336 pub direction: FirestoreQueryDirection,
338}
339
340impl FirestoreQueryOrder {
341 pub fn to_string_format(&self) -> String {
343 format!("{} {}", self.field_name, self.direction.to_string())
344 }
345}
346
347impl<S> From<(S, FirestoreQueryDirection)> for FirestoreQueryOrder
348where
349 S: AsRef<str>,
350{
351 fn from(field_order: (S, FirestoreQueryDirection)) -> Self {
352 FirestoreQueryOrder::new(field_order.0.as_ref().to_string(), field_order.1)
353 }
354}
355
356impl From<FirestoreQueryOrder> for structured_query::Order {
357 fn from(order: FirestoreQueryOrder) -> Self {
358 structured_query::Order {
359 field: Some(structured_query::FieldReference {
360 field_path: order.field_name,
361 }),
362 direction: (match order.direction {
363 FirestoreQueryDirection::Ascending => structured_query::Direction::Ascending.into(),
364 FirestoreQueryDirection::Descending => {
365 structured_query::Direction::Descending.into()
366 }
367 }),
368 }
369 }
370}
371
372#[derive(Debug, Eq, PartialEq, Clone)]
374pub enum FirestoreQueryDirection {
375 Ascending,
377 Descending,
379}
380
381#[allow(clippy::to_string_trait_impl)]
382impl ToString for FirestoreQueryDirection {
383 fn to_string(&self) -> String {
384 match self {
385 FirestoreQueryDirection::Ascending => "asc".to_string(),
386 FirestoreQueryDirection::Descending => "desc".to_string(),
387 }
388 }
389}
390
391#[derive(Debug, PartialEq, Clone, Builder)]
393pub struct FirestoreQueryFilterComposite {
394 pub for_all_filters: Vec<FirestoreQueryFilter>,
396 pub operator: FirestoreQueryFilterCompositeOperator,
398}
399
400#[derive(Debug, Eq, PartialEq, Clone)]
402pub enum FirestoreQueryFilterCompositeOperator {
403 And,
405 Or,
407}
408
409impl From<FirestoreQueryFilterCompositeOperator> for structured_query::composite_filter::Operator {
410 fn from(operator: FirestoreQueryFilterCompositeOperator) -> Self {
411 match operator {
412 FirestoreQueryFilterCompositeOperator::And => {
413 structured_query::composite_filter::Operator::And
414 }
415 FirestoreQueryFilterCompositeOperator::Or => {
416 structured_query::composite_filter::Operator::Or
417 }
418 }
419 }
420}
421
422#[derive(Debug, Eq, PartialEq, Clone)]
424pub enum FirestoreQueryFilterUnary {
425 IsNan(String),
428 IsNull(String),
431 IsNotNan(String),
434 IsNotNull(String),
437}
438
439#[derive(Debug, PartialEq, Clone)]
443pub enum FirestoreQueryFilterCompare {
444 LessThan(String, FirestoreValue),
446 LessThanOrEqual(String, FirestoreValue),
448 GreaterThan(String, FirestoreValue),
450 GreaterThanOrEqual(String, FirestoreValue),
452 Equal(String, FirestoreValue),
454 NotEqual(String, FirestoreValue),
456 ArrayContains(String, FirestoreValue),
458 In(String, FirestoreValue),
460 ArrayContainsAny(String, FirestoreValue),
463 NotIn(String, FirestoreValue),
465}
466
467#[derive(Debug, PartialEq, Clone)]
472pub enum FirestoreQueryCursor {
473 BeforeValue(Vec<FirestoreValue>),
476 AfterValue(Vec<FirestoreValue>),
479}
480
481impl From<FirestoreQueryCursor> for gcloud_sdk::google::firestore::v1::Cursor {
482 fn from(cursor: FirestoreQueryCursor) -> Self {
483 match cursor {
484 FirestoreQueryCursor::BeforeValue(values) => {
485 gcloud_sdk::google::firestore::v1::Cursor {
486 values: values.into_iter().map(|value| value.value).collect(),
487 before: true,
488 }
489 }
490 FirestoreQueryCursor::AfterValue(values) => gcloud_sdk::google::firestore::v1::Cursor {
491 values: values.into_iter().map(|value| value.value).collect(),
492 before: false,
493 },
494 }
495 }
496}
497
498impl From<gcloud_sdk::google::firestore::v1::Cursor> for FirestoreQueryCursor {
499 fn from(cursor: gcloud_sdk::google::firestore::v1::Cursor) -> Self {
500 let firestore_values = cursor
501 .values
502 .into_iter()
503 .map(FirestoreValue::from)
504 .collect();
505 if cursor.before {
506 FirestoreQueryCursor::BeforeValue(firestore_values)
507 } else {
508 FirestoreQueryCursor::AfterValue(firestore_values)
509 }
510 }
511}
512
513#[derive(Debug, PartialEq, Clone, Builder)]
518pub struct FirestorePartitionQueryParams {
519 pub query_params: FirestoreQueryParams,
521 pub partition_count: u32,
523 pub page_size: u32,
526 pub page_token: Option<String>,
528}
529
530#[derive(Debug, PartialEq, Clone, Builder)]
535pub struct FirestorePartition {
536 pub start_at: Option<FirestoreQueryCursor>,
538 pub end_at: Option<FirestoreQueryCursor>,
540}
541
542#[derive(Debug, PartialEq, Clone, Builder)]
547pub struct FirestoreExplainOptions {
548 pub analyze: Option<bool>,
551}
552
553impl TryFrom<&FirestoreExplainOptions> for gcloud_sdk::google::firestore::v1::ExplainOptions {
554 type Error = FirestoreError;
555 fn try_from(explain_options: &FirestoreExplainOptions) -> Result<Self, Self::Error> {
556 Ok(ExplainOptions {
557 analyze: explain_options.analyze.unwrap_or(false),
558 })
559 }
560}
561
562#[derive(Debug, PartialEq, Clone, Builder)]
566pub struct FirestoreFindNearestOptions {
567 pub field_name: String,
569 pub query_vector: FirestoreVector,
571 pub distance_measure: FirestoreFindNearestDistanceMeasure,
573 pub neighbors_limit: u32,
575 pub distance_result_field: Option<String>,
578 pub distance_threshold: Option<f64>,
581}
582
583impl TryFrom<FirestoreFindNearestOptions>
584 for gcloud_sdk::google::firestore::v1::structured_query::FindNearest
585{
586 type Error = FirestoreError;
587
588 fn try_from(options: FirestoreFindNearestOptions) -> Result<Self, Self::Error> {
589 Ok(structured_query::FindNearest {
590 vector_field: Some(structured_query::FieldReference {
591 field_path: options.field_name,
592 }),
593 query_vector: Some(Into::<FirestoreValue>::into(options.query_vector).value),
594 distance_measure: {
595 let distance_measure: structured_query::find_nearest::DistanceMeasure =
596 options.distance_measure.try_into()?;
597 distance_measure.into()
598 },
599 limit: Some(options.neighbors_limit.try_into().map_err(|e| {
600 FirestoreError::InvalidParametersError(FirestoreInvalidParametersError::new(
601 FirestoreInvalidParametersPublicDetails::new(
602 "neighbors_limit".to_string(),
603 format!(
604 "Invalid value for neighbors_limit: {}. Maximum allowed value is {}. Error: {}",
605 options.neighbors_limit,
606 i32::MAX,
607 e
608 ),
609 ),
610 ))
611 })?),
612 distance_result_field: options.distance_result_field.unwrap_or_default(),
613 distance_threshold: options.distance_threshold,
614 })
615 }
616}
617
618#[derive(Debug, PartialEq, Clone)]
620pub enum FirestoreFindNearestDistanceMeasure {
621 Euclidean,
623 Cosine,
625 DotProduct,
627}
628
629impl TryFrom<FirestoreFindNearestDistanceMeasure>
630 for structured_query::find_nearest::DistanceMeasure
631{
632 type Error = FirestoreError;
633
634 fn try_from(measure: FirestoreFindNearestDistanceMeasure) -> Result<Self, Self::Error> {
635 match measure {
636 FirestoreFindNearestDistanceMeasure::Euclidean => {
637 Ok(structured_query::find_nearest::DistanceMeasure::Euclidean)
638 }
639 FirestoreFindNearestDistanceMeasure::Cosine => {
640 Ok(structured_query::find_nearest::DistanceMeasure::Cosine)
641 }
642 FirestoreFindNearestDistanceMeasure::DotProduct => {
643 Ok(structured_query::find_nearest::DistanceMeasure::DotProduct)
644 }
645 }
646 }
647}