1#![allow(clippy::derive_partial_eq_without_eq)]
4
5use crate::errors::{
6 FirestoreError, FirestoreInvalidParametersError, FirestoreInvalidParametersPublicDetails,
7};
8use crate::{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
91impl TryFrom<FirestoreQueryParams> for StructuredQuery {
92 type Error = FirestoreError;
93
94 fn try_from(params: FirestoreQueryParams) -> Result<Self, Self::Error> {
95 let query_filter = params.filter.map(|f| f.into());
96
97 Ok(StructuredQuery {
98 select: params.return_only_fields.map(|select_only_fields| {
99 structured_query::Projection {
100 fields: select_only_fields
101 .into_iter()
102 .map(|field_name| structured_query::FieldReference {
103 field_path: field_name,
104 })
105 .collect(),
106 }
107 }),
108 start_at: params.start_at.map(|start_at| start_at.into()),
109 end_at: params.end_at.map(|end_at| end_at.into()),
110 limit: params.limit.map(|x| x as i32),
111 offset: params.offset.map(|x| x as i32).unwrap_or(0),
112 order_by: params
113 .order_by
114 .map(|po| po.into_iter().map(|fo| fo.into()).collect())
115 .unwrap_or_default(),
116 from: match params.collection_id {
117 FirestoreQueryCollection::Single(collection_id) => {
118 vec![structured_query::CollectionSelector {
119 collection_id,
120 all_descendants: params.all_descendants.unwrap_or(false),
121 }]
122 }
123 FirestoreQueryCollection::Group(collection_ids) => collection_ids
124 .into_iter()
125 .map(|collection_id| structured_query::CollectionSelector {
126 collection_id,
127 all_descendants: params.all_descendants.unwrap_or(false),
128 })
129 .collect(),
130 },
131 find_nearest: params
132 .find_nearest
133 .map(|find_nearest| find_nearest.try_into())
134 .transpose()?,
135 r#where: query_filter,
136 })
137 }
138}
139
140#[derive(Debug, PartialEq, Clone)]
145pub enum FirestoreQueryFilter {
146 Composite(FirestoreQueryFilterComposite),
148 Unary(FirestoreQueryFilterUnary),
150 Compare(Option<FirestoreQueryFilterCompare>),
154}
155
156impl From<FirestoreQueryFilter> for structured_query::Filter {
157 fn from(filter: FirestoreQueryFilter) -> Self {
158 let filter_type = match filter {
159 FirestoreQueryFilter::Compare(comp) => comp.map(|cmp| {
160 structured_query::filter::FilterType::FieldFilter(match cmp {
161 FirestoreQueryFilterCompare::Equal(field_name, fvalue) => {
162 structured_query::FieldFilter {
163 field: Some(structured_query::FieldReference {
164 field_path: field_name,
165 }),
166 op: structured_query::field_filter::Operator::Equal.into(),
167 value: Some(fvalue.value),
168 }
169 }
170 FirestoreQueryFilterCompare::NotEqual(field_name, fvalue) => {
171 structured_query::FieldFilter {
172 field: Some(structured_query::FieldReference {
173 field_path: field_name,
174 }),
175 op: structured_query::field_filter::Operator::NotEqual.into(),
176 value: Some(fvalue.value),
177 }
178 }
179 FirestoreQueryFilterCompare::In(field_name, fvalue) => {
180 structured_query::FieldFilter {
181 field: Some(structured_query::FieldReference {
182 field_path: field_name,
183 }),
184 op: structured_query::field_filter::Operator::In.into(),
185 value: Some(fvalue.value),
186 }
187 }
188 FirestoreQueryFilterCompare::NotIn(field_name, fvalue) => {
189 structured_query::FieldFilter {
190 field: Some(structured_query::FieldReference {
191 field_path: field_name,
192 }),
193 op: structured_query::field_filter::Operator::NotIn.into(),
194 value: Some(fvalue.value),
195 }
196 }
197 FirestoreQueryFilterCompare::ArrayContains(field_name, fvalue) => {
198 structured_query::FieldFilter {
199 field: Some(structured_query::FieldReference {
200 field_path: field_name,
201 }),
202 op: structured_query::field_filter::Operator::ArrayContains.into(),
203 value: Some(fvalue.value),
204 }
205 }
206 FirestoreQueryFilterCompare::ArrayContainsAny(field_name, fvalue) => {
207 structured_query::FieldFilter {
208 field: Some(structured_query::FieldReference {
209 field_path: field_name,
210 }),
211 op: structured_query::field_filter::Operator::ArrayContainsAny.into(),
212 value: Some(fvalue.value),
213 }
214 }
215 FirestoreQueryFilterCompare::LessThan(field_name, fvalue) => {
216 structured_query::FieldFilter {
217 field: Some(structured_query::FieldReference {
218 field_path: field_name,
219 }),
220 op: structured_query::field_filter::Operator::LessThan.into(),
221 value: Some(fvalue.value),
222 }
223 }
224 FirestoreQueryFilterCompare::LessThanOrEqual(field_name, fvalue) => {
225 structured_query::FieldFilter {
226 field: Some(structured_query::FieldReference {
227 field_path: field_name,
228 }),
229 op: structured_query::field_filter::Operator::LessThanOrEqual.into(),
230 value: Some(fvalue.value),
231 }
232 }
233 FirestoreQueryFilterCompare::GreaterThan(field_name, fvalue) => {
234 structured_query::FieldFilter {
235 field: Some(structured_query::FieldReference {
236 field_path: field_name,
237 }),
238 op: structured_query::field_filter::Operator::GreaterThan.into(),
239 value: Some(fvalue.value),
240 }
241 }
242 FirestoreQueryFilterCompare::GreaterThanOrEqual(field_name, fvalue) => {
243 structured_query::FieldFilter {
244 field: Some(structured_query::FieldReference {
245 field_path: field_name,
246 }),
247 op: structured_query::field_filter::Operator::GreaterThanOrEqual.into(),
248 value: Some(fvalue.value),
249 }
250 }
251 })
252 }),
253 FirestoreQueryFilter::Composite(composite) => {
254 Some(structured_query::filter::FilterType::CompositeFilter(
255 structured_query::CompositeFilter {
256 op: (Into::<structured_query::composite_filter::Operator>::into(
257 composite.operator,
258 ))
259 .into(),
260 filters: composite
261 .for_all_filters
262 .into_iter()
263 .map(structured_query::Filter::from)
264 .filter(|filter| filter.filter_type.is_some())
265 .collect(),
266 },
267 ))
268 }
269 FirestoreQueryFilter::Unary(unary) => match unary {
270 FirestoreQueryFilterUnary::IsNan(field_name) => {
271 Some(structured_query::filter::FilterType::UnaryFilter(
272 structured_query::UnaryFilter {
273 op: structured_query::unary_filter::Operator::IsNan.into(),
274 operand_type: Some(structured_query::unary_filter::OperandType::Field(
275 structured_query::FieldReference {
276 field_path: field_name,
277 },
278 )),
279 },
280 ))
281 }
282 FirestoreQueryFilterUnary::IsNull(field_name) => {
283 Some(structured_query::filter::FilterType::UnaryFilter(
284 structured_query::UnaryFilter {
285 op: structured_query::unary_filter::Operator::IsNull.into(),
286 operand_type: Some(structured_query::unary_filter::OperandType::Field(
287 structured_query::FieldReference {
288 field_path: field_name,
289 },
290 )),
291 },
292 ))
293 }
294 FirestoreQueryFilterUnary::IsNotNan(field_name) => {
295 Some(structured_query::filter::FilterType::UnaryFilter(
296 structured_query::UnaryFilter {
297 op: structured_query::unary_filter::Operator::IsNotNan.into(),
298 operand_type: Some(structured_query::unary_filter::OperandType::Field(
299 structured_query::FieldReference {
300 field_path: field_name,
301 },
302 )),
303 },
304 ))
305 }
306 FirestoreQueryFilterUnary::IsNotNull(field_name) => {
307 Some(structured_query::filter::FilterType::UnaryFilter(
308 structured_query::UnaryFilter {
309 op: structured_query::unary_filter::Operator::IsNotNull.into(),
310 operand_type: Some(structured_query::unary_filter::OperandType::Field(
311 structured_query::FieldReference {
312 field_path: field_name,
313 },
314 )),
315 },
316 ))
317 }
318 },
319 };
320
321 structured_query::Filter { filter_type }
322 }
323}
324
325#[derive(Debug, Eq, PartialEq, Clone, Builder)]
327pub struct FirestoreQueryOrder {
328 pub field_name: String,
330 pub direction: FirestoreQueryDirection,
332}
333
334impl FirestoreQueryOrder {
335 pub fn to_string_format(&self) -> String {
337 format!("{} {}", self.field_name, self.direction.to_string())
338 }
339}
340
341impl<S> From<(S, FirestoreQueryDirection)> for FirestoreQueryOrder
342where
343 S: AsRef<str>,
344{
345 fn from(field_order: (S, FirestoreQueryDirection)) -> Self {
346 FirestoreQueryOrder::new(field_order.0.as_ref().to_string(), field_order.1)
347 }
348}
349
350impl From<FirestoreQueryOrder> for structured_query::Order {
351 fn from(order: FirestoreQueryOrder) -> Self {
352 structured_query::Order {
353 field: Some(structured_query::FieldReference {
354 field_path: order.field_name,
355 }),
356 direction: (match order.direction {
357 FirestoreQueryDirection::Ascending => structured_query::Direction::Ascending.into(),
358 FirestoreQueryDirection::Descending => {
359 structured_query::Direction::Descending.into()
360 }
361 }),
362 }
363 }
364}
365
366#[derive(Debug, Eq, PartialEq, Clone)]
368pub enum FirestoreQueryDirection {
369 Ascending,
371 Descending,
373}
374
375#[allow(clippy::to_string_trait_impl)]
376impl ToString for FirestoreQueryDirection {
377 fn to_string(&self) -> String {
378 match self {
379 FirestoreQueryDirection::Ascending => "asc".to_string(),
380 FirestoreQueryDirection::Descending => "desc".to_string(),
381 }
382 }
383}
384
385#[derive(Debug, PartialEq, Clone, Builder)]
387pub struct FirestoreQueryFilterComposite {
388 pub for_all_filters: Vec<FirestoreQueryFilter>,
390 pub operator: FirestoreQueryFilterCompositeOperator,
392}
393
394#[derive(Debug, Eq, PartialEq, Clone)]
396pub enum FirestoreQueryFilterCompositeOperator {
397 And,
399 Or,
401}
402
403impl From<FirestoreQueryFilterCompositeOperator> for structured_query::composite_filter::Operator {
404 fn from(operator: FirestoreQueryFilterCompositeOperator) -> Self {
405 match operator {
406 FirestoreQueryFilterCompositeOperator::And => {
407 structured_query::composite_filter::Operator::And
408 }
409 FirestoreQueryFilterCompositeOperator::Or => {
410 structured_query::composite_filter::Operator::Or
411 }
412 }
413 }
414}
415
416#[derive(Debug, Eq, PartialEq, Clone)]
418pub enum FirestoreQueryFilterUnary {
419 IsNan(String),
422 IsNull(String),
425 IsNotNan(String),
428 IsNotNull(String),
431}
432
433#[derive(Debug, PartialEq, Clone)]
437pub enum FirestoreQueryFilterCompare {
438 LessThan(String, FirestoreValue),
440 LessThanOrEqual(String, FirestoreValue),
442 GreaterThan(String, FirestoreValue),
444 GreaterThanOrEqual(String, FirestoreValue),
446 Equal(String, FirestoreValue),
448 NotEqual(String, FirestoreValue),
450 ArrayContains(String, FirestoreValue),
452 In(String, FirestoreValue),
454 ArrayContainsAny(String, FirestoreValue),
457 NotIn(String, FirestoreValue),
459}
460
461#[derive(Debug, PartialEq, Clone)]
466pub enum FirestoreQueryCursor {
467 BeforeValue(Vec<FirestoreValue>),
470 AfterValue(Vec<FirestoreValue>),
473}
474
475impl From<FirestoreQueryCursor> for gcloud_sdk::google::firestore::v1::Cursor {
476 fn from(cursor: FirestoreQueryCursor) -> Self {
477 match cursor {
478 FirestoreQueryCursor::BeforeValue(values) => {
479 gcloud_sdk::google::firestore::v1::Cursor {
480 values: values.into_iter().map(|value| value.value).collect(),
481 before: true,
482 }
483 }
484 FirestoreQueryCursor::AfterValue(values) => gcloud_sdk::google::firestore::v1::Cursor {
485 values: values.into_iter().map(|value| value.value).collect(),
486 before: false,
487 },
488 }
489 }
490}
491
492impl From<gcloud_sdk::google::firestore::v1::Cursor> for FirestoreQueryCursor {
493 fn from(cursor: gcloud_sdk::google::firestore::v1::Cursor) -> Self {
494 let firestore_values = cursor
495 .values
496 .into_iter()
497 .map(FirestoreValue::from)
498 .collect();
499 if cursor.before {
500 FirestoreQueryCursor::BeforeValue(firestore_values)
501 } else {
502 FirestoreQueryCursor::AfterValue(firestore_values)
503 }
504 }
505}
506
507#[derive(Debug, PartialEq, Clone, Builder)]
512pub struct FirestorePartitionQueryParams {
513 pub query_params: FirestoreQueryParams,
515 pub partition_count: u32,
517 pub page_size: u32,
520 pub page_token: Option<String>,
522}
523
524#[derive(Debug, PartialEq, Clone, Builder)]
529pub struct FirestorePartition {
530 pub start_at: Option<FirestoreQueryCursor>,
532 pub end_at: Option<FirestoreQueryCursor>,
534}
535
536#[derive(Debug, PartialEq, Clone, Builder)]
541pub struct FirestoreExplainOptions {
542 pub analyze: Option<bool>,
545}
546
547impl TryFrom<&FirestoreExplainOptions> for gcloud_sdk::google::firestore::v1::ExplainOptions {
548 type Error = FirestoreError;
549 fn try_from(explain_options: &FirestoreExplainOptions) -> Result<Self, Self::Error> {
550 Ok(ExplainOptions {
551 analyze: explain_options.analyze.unwrap_or(false),
552 })
553 }
554}
555
556#[derive(Debug, PartialEq, Clone, Builder)]
560pub struct FirestoreFindNearestOptions {
561 pub field_name: String,
563 pub query_vector: FirestoreVector,
565 pub distance_measure: FirestoreFindNearestDistanceMeasure,
567 pub neighbors_limit: u32,
569 pub distance_result_field: Option<String>,
572 pub distance_threshold: Option<f64>,
575}
576
577impl TryFrom<FirestoreFindNearestOptions>
578 for gcloud_sdk::google::firestore::v1::structured_query::FindNearest
579{
580 type Error = FirestoreError;
581
582 fn try_from(options: FirestoreFindNearestOptions) -> Result<Self, Self::Error> {
583 Ok(structured_query::FindNearest {
584 vector_field: Some(structured_query::FieldReference {
585 field_path: options.field_name,
586 }),
587 query_vector: Some(Into::<FirestoreValue>::into(options.query_vector).value),
588 distance_measure: {
589 let distance_measure: structured_query::find_nearest::DistanceMeasure =
590 options.distance_measure.try_into()?;
591 distance_measure.into()
592 },
593 limit: Some(options.neighbors_limit.try_into().map_err(|e| {
594 FirestoreError::InvalidParametersError(FirestoreInvalidParametersError::new(
595 FirestoreInvalidParametersPublicDetails::new(
596 "neighbors_limit".to_string(),
597 format!(
598 "Invalid value for neighbors_limit: {}. Maximum allowed value is {}. Error: {}",
599 options.neighbors_limit,
600 i32::MAX,
601 e
602 ),
603 ),
604 ))
605 })?),
606 distance_result_field: options.distance_result_field.unwrap_or_default(),
607 distance_threshold: options.distance_threshold,
608 })
609 }
610}
611
612#[derive(Debug, PartialEq, Clone)]
614pub enum FirestoreFindNearestDistanceMeasure {
615 Euclidean,
617 Cosine,
619 DotProduct,
621}
622
623impl TryFrom<FirestoreFindNearestDistanceMeasure>
624 for structured_query::find_nearest::DistanceMeasure
625{
626 type Error = FirestoreError;
627
628 fn try_from(measure: FirestoreFindNearestDistanceMeasure) -> Result<Self, Self::Error> {
629 match measure {
630 FirestoreFindNearestDistanceMeasure::Euclidean => {
631 Ok(structured_query::find_nearest::DistanceMeasure::Euclidean)
632 }
633 FirestoreFindNearestDistanceMeasure::Cosine => {
634 Ok(structured_query::find_nearest::DistanceMeasure::Cosine)
635 }
636 FirestoreFindNearestDistanceMeasure::DotProduct => {
637 Ok(structured_query::find_nearest::DistanceMeasure::DotProduct)
638 }
639 }
640 }
641}