Skip to main content

firestore/db/
query_models.rs

1// Allow derive_partial_eq_without_eq because some of these types wrap generated gRPC types
2// that might not implement Eq, or their Eq implementation might change.
3#![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/// Specifies the target collection(s) for a Firestore query.
13#[derive(Debug, Eq, PartialEq, Clone)]
14pub enum FirestoreQueryCollection {
15    /// Queries a single collection identified by its ID.
16    Single(String),
17    /// Performs a collection group query across all collections with the specified ID(s).
18    /// While Firestore gRPC supports multiple collection IDs here, typically a collection group query
19    /// targets all collections with *one* specific ID.
20    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/// Parameters for constructing and executing a Firestore query.
40///
41/// This struct encapsulates all configurable aspects of a query, such as the
42/// target collection, filters, ordering, limits, offsets, cursors, and projections.
43/// It is used by the fluent API and direct query methods to define the query to be sent to Firestore.
44#[derive(Debug, PartialEq, Clone, Builder)]
45pub struct FirestoreQueryParams {
46    /// The parent resource path. For top-level collections, this is typically
47    /// the database path (e.g., "projects/my-project/databases/(default)/documents").
48    /// For sub-collections, it's the path to the parent document.
49    /// If `None`, the query is assumed to be on a top-level collection relative to the
50    /// `FirestoreDb`'s document path.
51    pub parent: Option<String>,
52
53    /// The ID of the collection or collection group to query.
54    pub collection_id: FirestoreQueryCollection,
55
56    /// The maximum number of results to return.
57    pub limit: Option<u32>,
58
59    /// The number of results to skip.
60    pub offset: Option<u32>,
61
62    /// A list of fields and directions to order the results by.
63    pub order_by: Option<Vec<FirestoreQueryOrder>>,
64
65    /// The filter to apply to the query.
66    pub filter: Option<FirestoreQueryFilter>,
67
68    /// If `true`, the query will search all collections located anywhere in the
69    /// database under the `parent` path (if specified) that have the given
70    /// `collection_id`. This is used for collection group queries.
71    /// Defaults to `false` if not set, meaning only direct children collections are queried.
72    pub all_descendants: Option<bool>,
73
74    /// If set, only these fields will be returned in the query results (projection).
75    /// If `None`, all fields are returned.
76    pub return_only_fields: Option<Vec<String>>,
77
78    /// A cursor to define the starting point of the query.
79    pub start_at: Option<FirestoreQueryCursor>,
80
81    /// A cursor to define the ending point of the query.
82    pub end_at: Option<FirestoreQueryCursor>,
83
84    /// Options for requesting an explanation of the query execution plan.
85    pub explain_options: Option<FirestoreExplainOptions>,
86
87    /// Options for performing a vector similarity search (find nearest neighbors).
88    pub find_nearest: Option<FirestoreFindNearestOptions>,
89
90    /// Request options (e.g. request tags) for this query.
91    ///
92    /// Overrides any session wide default configured with
93    /// [`FirestoreDb::clone_with_request_options`](crate::FirestoreDb::clone_with_request_options).
94    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/// Represents a filter condition for a Firestore query.
147///
148/// Filters are used to narrow down the documents returned by a query based on
149/// conditions applied to their fields.
150#[derive(Debug, PartialEq, Clone)]
151pub enum FirestoreQueryFilter {
152    /// A composite filter that combines multiple sub-filters using an operator (AND/OR).
153    Composite(FirestoreQueryFilterComposite),
154    /// A unary filter that applies an operation to a single field (e.g., IS NULL, IS NAN).
155    Unary(FirestoreQueryFilterUnary),
156    /// A field filter that compares a field to a value (e.g., equality, greater than).
157    /// The `Option` allows for representing an effectively empty or no-op filter,
158    /// which can be useful in dynamic filter construction.
159    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/// Specifies an ordering for query results based on a field.
332#[derive(Debug, Eq, PartialEq, Clone, Builder)]
333pub struct FirestoreQueryOrder {
334    /// The path to the field to order by (e.g., "name", "address.city").
335    pub field_name: String,
336    /// The direction of the ordering (ascending or descending).
337    pub direction: FirestoreQueryDirection,
338}
339
340impl FirestoreQueryOrder {
341    /// Returns a string representation of the order, e.g., "fieldName asc".
342    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/// The direction for ordering query results.
373#[derive(Debug, Eq, PartialEq, Clone)]
374pub enum FirestoreQueryDirection {
375    /// Sort results in ascending order.
376    Ascending,
377    /// Sort results in descending order.
378    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/// A composite filter that combines multiple [`FirestoreQueryFilter`]s.
392#[derive(Debug, PartialEq, Clone, Builder)]
393pub struct FirestoreQueryFilterComposite {
394    /// The list of sub-filters to combine.
395    pub for_all_filters: Vec<FirestoreQueryFilter>,
396    /// The operator used to combine the sub-filters (AND/OR).
397    pub operator: FirestoreQueryFilterCompositeOperator,
398}
399
400/// The operator for combining filters in a [`FirestoreQueryFilterComposite`].
401#[derive(Debug, Eq, PartialEq, Clone)]
402pub enum FirestoreQueryFilterCompositeOperator {
403    /// Logical AND: all sub-filters must be true.
404    And,
405    /// Logical OR: at least one sub-filter must be true.
406    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/// A unary filter that applies an operation to a single field.
423#[derive(Debug, Eq, PartialEq, Clone)]
424pub enum FirestoreQueryFilterUnary {
425    /// Checks if a field's value is NaN (Not a Number).
426    /// The string argument is the field path.
427    IsNan(String),
428    /// Checks if a field's value is NULL.
429    /// The string argument is the field path.
430    IsNull(String),
431    /// Checks if a field's value is not NaN.
432    /// The string argument is the field path.
433    IsNotNan(String),
434    /// Checks if a field's value is not NULL.
435    /// The string argument is the field path.
436    IsNotNull(String),
437}
438
439/// A field filter that compares a field to a value using a specific operator.
440/// The first `String` argument in each variant is the field path.
441/// The `FirestoreValue` is the value to compare against.
442#[derive(Debug, PartialEq, Clone)]
443pub enum FirestoreQueryFilterCompare {
444    /// Field is less than the value.
445    LessThan(String, FirestoreValue),
446    /// Field is less than or equal to the value.
447    LessThanOrEqual(String, FirestoreValue),
448    /// Field is greater than the value.
449    GreaterThan(String, FirestoreValue),
450    /// Field is greater than or equal to the value.
451    GreaterThanOrEqual(String, FirestoreValue),
452    /// Field is equal to the value.
453    Equal(String, FirestoreValue),
454    /// Field is not equal to the value.
455    NotEqual(String, FirestoreValue),
456    /// Field (which must be an array) contains the value.
457    ArrayContains(String, FirestoreValue),
458    /// Field's value is IN the given array value. The `FirestoreValue` should be an array.
459    In(String, FirestoreValue),
460    /// Field (which must be an array) contains any of the values in the given array value.
461    /// The `FirestoreValue` should be an array.
462    ArrayContainsAny(String, FirestoreValue),
463    /// Field's value is NOT IN the given array value. The `FirestoreValue` should be an array.
464    NotIn(String, FirestoreValue),
465}
466
467/// Represents a cursor for paginating query results.
468///
469/// Cursors define a starting or ending point for a query based on the values
470/// of the fields being ordered by.
471#[derive(Debug, PartialEq, Clone)]
472pub enum FirestoreQueryCursor {
473    /// Starts the query results before the document that has these field values.
474    /// The `Vec<FirestoreValue>` corresponds to the values of the ordered fields.
475    BeforeValue(Vec<FirestoreValue>),
476    /// Starts the query results after the document that has these field values.
477    /// The `Vec<FirestoreValue>` corresponds to the values of the ordered fields.
478    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/// Parameters for a partitioned query.
514///
515/// Partitioned queries allow you to divide a large query into smaller, parallelizable chunks.
516/// This is useful for exporting data or performing large-scale data processing.
517#[derive(Debug, PartialEq, Clone, Builder)]
518pub struct FirestorePartitionQueryParams {
519    /// The base query parameters to partition.
520    pub query_params: FirestoreQueryParams,
521    /// The desired number of partitions to return. Must be a positive integer.
522    pub partition_count: u32,
523    /// The maximum number of partitions to return in this call, used for paging.
524    /// Must be a positive integer.
525    pub page_size: u32,
526    /// A page token from a previous `PartitionQuery` response to retrieve the next set of partitions.
527    pub page_token: Option<String>,
528}
529
530/// Represents a single partition of a query.
531///
532/// Each partition defines a range of the original query using `start_at` and `end_at` cursors.
533/// Executing a query with these cursors will yield the documents for that specific partition.
534#[derive(Debug, PartialEq, Clone, Builder)]
535pub struct FirestorePartition {
536    /// The cursor indicating the start of this partition.
537    pub start_at: Option<FirestoreQueryCursor>,
538    /// The cursor indicating the end of this partition.
539    pub end_at: Option<FirestoreQueryCursor>,
540}
541
542/// Options for requesting query execution analysis from Firestore.
543///
544/// When `analyze` is true, Firestore will return detailed information about
545/// how the query was executed, including index usage and performance metrics.
546#[derive(Debug, PartialEq, Clone, Builder)]
547pub struct FirestoreExplainOptions {
548    /// If `true`, Firestore will analyze the query and return execution details.
549    /// Defaults to `false` if not specified.
550    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/// Options for performing a vector similarity search (find nearest neighbors).
563///
564/// This is used to find documents whose vector field is closest to a given query vector.
565#[derive(Debug, PartialEq, Clone, Builder)]
566pub struct FirestoreFindNearestOptions {
567    /// The path to the vector field in your documents to search against.
568    pub field_name: String,
569    /// The query vector to find nearest neighbors for.
570    pub query_vector: FirestoreVector,
571    /// The distance measure to use for comparing vectors.
572    pub distance_measure: FirestoreFindNearestDistanceMeasure,
573    /// The maximum number of nearest neighbors to return.
574    pub neighbors_limit: u32,
575    /// An optional field name to store the calculated distance in the query results.
576    /// If provided, each returned document will include this field with the distance value.
577    pub distance_result_field: Option<String>,
578    /// An optional threshold for the distance. Only neighbors within this distance
579    /// will be returned.
580    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/// Specifies the distance measure for vector similarity searches.
619#[derive(Debug, PartialEq, Clone)]
620pub enum FirestoreFindNearestDistanceMeasure {
621    /// Euclidean distance.
622    Euclidean,
623    /// Cosine similarity (measures the cosine of the angle between two vectors).
624    Cosine,
625    /// Dot product distance.
626    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}