Skip to main content

es_entity/
pagination.rs

1//! Control and customize the query execution and its response.
2
3/// Controls the sorting order when listing the entities from the database
4///
5/// `ListDirection` enum is used to specify order when listing entities from the database using [`EsRepo`][crate::EsRepo]
6/// generated functions like `list_by`, `list_for` and `list_for_filters`. Has two variants: `Ascending` and `Descending`
7///
8/// # Examples
9///
10/// ```ignore
11/// // List users by ID in ascending order (oldest first)
12/// let paginated_users_asc = users.list_by_id(
13///     PaginatedQueryArgs { first: 5, after: None },
14///     ListDirection::Ascending, // or just Default::default()
15/// ).await?
16///
17/// // List users by name in descending order (Z to A)
18/// let paginated_users_desc = users.list_by_name(
19///     PaginatedQueryArgs { first: 5, after: None },
20///     ListDirection::Descending,
21/// ).await?
22/// ```
23#[derive(Default, std::fmt::Debug, Clone, Copy)]
24pub enum ListDirection {
25    /// Sets the default direction variant to `Ascending`
26    #[default]
27    Ascending,
28    Descending,
29}
30
31/// Structure to sort entities on a specific field when listing from database
32///
33/// Sort enum is used to specify the sorting order and the field to sort the entities by when listing them using `list_for_filters`
34/// generated by [`EsRepo`][crate::EsRepo]. It is generated automatically for all fields which have `list_by` option. It encapsulates two fields,
35/// first is `by` to specify the field name, second is `direction` which takes [`ListDirection`]
36///
37/// # Example
38///
39/// ```ignore
40/// let result = users.list_for_filters(
41///     UsersFilters {
42///         name: Some("Murphy".to_string()),
43///     },
44///     Sort {
45///         // `UsersSortBy::Id` and `UsersSortBy::CreatedAt` are created by default,
46///         // other columns need `list_by` to sort by
47///         by: UsersSortBy::Id,
48///         direction: ListDirection::Descending,
49///     },
50///     PaginatedQueryArgs {
51///         first: 10,
52///         after: Default::default(),
53///     },
54/// )
55/// .await?;
56/// ```
57#[derive(std::fmt::Debug, Clone, Copy)]
58pub struct Sort<T> {
59    // T parameter represents the field
60    pub by: T,
61    pub direction: ListDirection,
62}
63
64/// A cursor-based pagination structure for efficiently paginating through large datasets
65///
66/// The `PaginatedQueryArgs<T>` encapsulates a `first` field that specifies the count of entities to fetch per query, and an optional `after` field
67/// that specifies the cursor to start from for the current query. The `<T>` parameter represents cursor type, which depends on the sorting field.
68/// A field's cursor is generated by [`EsRepo`][crate::EsRepo] if it has `list_by` option.
69///
70/// # Examples
71///
72/// ```ignore
73/// // Initial query - fetch first 10 users after an existing user id
74/// let query_args = PaginatedQueryArgs {
75///     first: 10,
76///     after: Some(user_cursor::UsersByIdCursor {
77///         id: some_existing_user_id // assume this variable exists
78///     }),
79/// };
80///
81/// // Execute query using `query_args` argument of `PaginatedQueryArgs` type
82/// let result = users.list_by_id(query_args, ListDirection::Ascending).await?;
83///
84/// // Continue pagination using the updated `next_query_args` of `PaginatedQueryArgs` type
85/// if result.has_next_page {
86///     let next_query_args = PaginatedQueryArgs {
87///         first: 10,
88///         after: result.end_cursor, // Use cursor from previous result to update `after`
89///     };
90///     let next_result = users.list_by_id(next_query_args, ListDirection::Ascending).await?;
91/// }
92/// ```
93#[derive(Debug)]
94pub struct PaginatedQueryArgs<T: std::fmt::Debug> {
95    /// Specifies the number of entities to fetch per query
96    pub first: usize,
97    /// Specifies the cursor/marker to start from for current query
98    pub after: Option<T>,
99}
100
101impl<T: std::fmt::Debug> Clone for PaginatedQueryArgs<T>
102where
103    T: Clone,
104{
105    fn clone(&self) -> Self {
106        Self {
107            first: self.first,
108            after: self.after.clone(),
109        }
110    }
111}
112
113impl<T: std::fmt::Debug> Default for PaginatedQueryArgs<T> {
114    /// Default value fetches first 100 entities
115    fn default() -> Self {
116        Self {
117            first: 100,
118            after: None,
119        }
120    }
121}
122
123/// Return type for paginated queries containing entities and pagination metadata
124///
125/// `PaginatedQueryRet` contains the fetched entities and utilities for continuing pagination.
126/// Returned by the [`EsRepo`][crate::EsRepo] functions like `list_by`, `list_for` and `list_for_filters`.
127/// Used with [`PaginatedQueryArgs`] to perform consistent and efficient pagination
128///
129/// # Examples
130///
131/// ```ignore
132/// let query_args = PaginatedQueryArgs {
133///     first: 10,
134///     after: None,
135/// };
136///
137/// // Execute query and get the `result` of type `PaginatedQueryRet`
138/// let result = users.list_by_id(query_args, ListDirection::Ascending).await?;
139///
140/// // Continue pagination using the `next_query_args` argument updated using result
141/// // Will continue only if 'has_next_page` returned from `result` is true
142/// if result.has_next_page {
143///     let next_query_args = PaginatedQueryArgs {
144///         first: 10,
145///         after: result.end_cursor, // update with 'end_cursor' of previous result
146///     };
147///     let next_result = users.list_by_id(next_query_args, ListDirection::Ascending).await?;
148/// }
149///
150/// // Or use PaginatedQueryRet::into_next_query() convenience method
151/// if let Some(next_query_args) = result.into_next_query() {
152///     let next_result = users.list_by_id(next_query_args, ListDirection::Ascending).await?;
153/// }
154/// ```
155pub struct PaginatedQueryRet<T, C> {
156    /// [Vec] for the fetched `entities` by the paginated query
157    pub entities: Vec<T>,
158    /// [bool] for indicating if the list has been exhausted or more entities can be fetched
159    pub has_next_page: bool,
160    /// cursor on the last entity fetched to continue paginated queries.
161    pub end_cursor: Option<C>,
162}
163
164impl<T, C> PaginatedQueryRet<T, C> {
165    /// Convenience method to create next query args if more pages are available
166    pub fn into_next_query(self) -> Option<PaginatedQueryArgs<C>>
167    where
168        C: std::fmt::Debug,
169    {
170        if self.has_next_page {
171            Some(PaginatedQueryArgs {
172                first: self.entities.len(),
173                after: self.end_cursor,
174            })
175        } else {
176            None
177        }
178    }
179}