How to check if there is a next/previous page with cursor_by pagination? #2765
-
|
For the same direction -- next/forward and prev/backward, it's easy, I just have to query one more and count the number of items. If I actually get one more, it means that there is another page. For the other direction, I don't know how to do without running another query, and it looks like sea-orm doesn't provide any helper for this. I wish instead of returning a page only, the cursor_by pagination would return something like: struct CursorByResult<Model, TupleValue> {
page: Vec<Model>,
forward_cursor: Option<TupleValue>, // None if no page
backward_cursor: Option<TupleValue>, // None if no page
}so that I could use the result without further shenanigans. What are my options? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
|
We canβt know whether thereβs a previous or next page without running an additional query. |
Beta Was this translation helpful? Give feedback.
-
|
I found a way to do it without a second request. My pagination structure is like this: pub struct Pagination<T> {
cursor: Option<Cursor<T>>,
page_size: u64,
is_forward: bool,
}Let's say that
Concretely, it means that every new cursor needs the previous one to be built, so I have a method like this: impl<T> Pagination<T> {
pub fn to_response(
self,
mut page: VecDeque<<Cursor<T> as CursorUpdatable>::Model>,
) -> PaginatedResponse<<Cursor<T> as CursorUpdatable>::Model>
where
Cursor<T>: CursorUpdatable,
T: Default + Serialize,
{
let (has_next_page, has_prev_page) = if self.order() == Order::Asc {
(page.len() as u64 > self.page_size(), self.cursor.is_some())
} else {
(self.cursor.is_some(), page.len() as u64 > self.page_size())
};
if self.order() == Order::Asc && has_next_page {
let _ = page.pop_back();
} else if self.order() == Order::Desc && has_prev_page {
let _ = page.pop_front();
}
let cursor = self.cursor.unwrap_or_default();
let start_cursor = page
.front()
.filter(|_| has_prev_page)
.map(|first| cursor.new_from(first).encode());
let end_cursor = page
.back()
.filter(|_| has_next_page)
.map(|last| cursor.new_from(last).encode());
PaginatedResponse {
page,
start_cursor,
end_cursor,
}
}
}I hope that this post can help people doing this for the first time and struggling to find an elegant solution. There is one last thing to figure out: the corner case where some items are deleted is not handled by sear-orm. Let's say I query 5 items, |
Beta Was this translation helpful? Give feedback.
I found a way to do it without a second request. My pagination structure is like this:
Let's say that
is_forwardis true: for the very first page, the cursor is set toNone(I use id = 0 or name = '' in that case internally). When I navigate forward, then now the cursor is set to something.page_size + 1. If there are indeedpage_size + 1results, it means that there is a next page. Then I pop the last result, and use the new last item as cursor.cursorandis_forwardfrom the request. Ifis_forwardβ¦