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}