Skip to content

Commit 4289fa1

Browse files
committed
Refactor pagination to prepare for expanding the scope of seek-based pagination on search
1 parent 71b8f8b commit 4289fa1

File tree

3 files changed

+41
-21
lines changed

3 files changed

+41
-21
lines changed

src/controllers/helpers/pagination.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,31 @@ impl<T> Paginated<T> {
198198
Some(opts)
199199
}
200200

201+
pub(crate) fn next_seek_params<S, F>(&self, f: F) -> AppResult<Option<IndexMap<String, String>>>
202+
where
203+
F: Fn(&T) -> S,
204+
S: Serialize,
205+
{
206+
if self.is_explicit_page() || self.records_and_total.len() < self.options.per_page as usize
207+
{
208+
return Ok(None);
209+
}
210+
211+
let mut opts = IndexMap::new();
212+
match self.options.page {
213+
Page::Unspecified | Page::Seek(_) => {
214+
let seek = f(&self.records_and_total.last().unwrap().record);
215+
opts.insert("seek".into(), encode_seek(seek)?);
216+
}
217+
Page::Numeric(_) => unreachable!(),
218+
};
219+
Ok(Some(opts))
220+
}
221+
222+
fn is_explicit_page(&self) -> bool {
223+
matches!(&self.options.page, Page::Numeric(_))
224+
}
225+
201226
pub(crate) fn iter(&self) -> impl Iterator<Item = &T> {
202227
self.records_and_total.iter().map(|row| &row.record)
203228
}

src/controllers/krate/search.rs

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use crate::auth::AuthCheck;
44
use diesel::dsl::*;
55
use diesel::sql_types::Array;
66
use diesel_full_text_search::*;
7-
use indexmap::IndexMap;
87
use once_cell::sync::OnceCell;
98

109
use crate::controllers::cargo_prelude::*;
@@ -150,7 +149,6 @@ pub async fn search(app: AppState, req: Parts) -> AppResult<Json<Value>> {
150149
let (total, next_page, prev_page, data, conn) = if supports_seek && !explicit_page {
151150
// Equivalent of:
152151
// `WHERE name > (SELECT name FROM crates WHERE id = $1) LIMIT $2`
153-
query = query.limit(pagination.per_page);
154152
if let Some(seek) = seek {
155153
let crate_name: String = crates::table
156154
.find(seek)
@@ -165,26 +163,22 @@ pub async fn search(app: AppState, req: Parts) -> AppResult<Json<Value>> {
165163
//
166164
// If this becomes a problem in the future the crates count could be denormalized, at least
167165
// for the filterless happy path.
168-
let count_query = filter_params.make_query(&req, conn)?.count();
169-
let total: i64 = info_span!("db.query", message = "SELECT COUNT(*) FROM crates")
170-
.in_scope(|| count_query.get_result(conn))?;
171-
172-
let results: Vec<(Crate, bool, Option<i64>)> =
173-
info_span!("db.query", message = "SELECT ... FROM crates")
166+
let query = query.pages_pagination_with_count_query(
167+
pagination,
168+
filter_params.make_query(&req, conn)?.count(),
169+
);
170+
let data: Paginated<(Crate, bool, Option<i64>)> =
171+
info_span!("db.query", message = "SELECT ..., COUNT(*) FROM crates")
174172
.in_scope(|| query.load(conn))?;
175173

176-
let next_page = if let Some(last) = results.last() {
177-
let mut params = IndexMap::new();
178-
params.insert(
179-
"seek".into(),
180-
crate::controllers::helpers::pagination::encode_seek(last.0.id)?,
181-
);
182-
Some(req.query_with_params(params))
183-
} else {
184-
None
185-
};
186-
187-
(total, next_page, None, results, conn)
174+
(
175+
data.total(),
176+
data.next_seek_params(|last| last.0.id)?
177+
.map(|p| req.query_with_params(p)),
178+
None,
179+
data.into_iter().collect::<Vec<_>>(),
180+
conn,
181+
)
188182
} else {
189183
let query = query.pages_pagination_with_count_query(
190184
pagination,

src/tests/routes/crates/list.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -791,12 +791,13 @@ fn seek_based_pagination() {
791791
if let Some(new_url) = resp.meta.next_page {
792792
assert_that!(resp.crates, len(eq(1)));
793793
url = Some(new_url);
794+
assert_eq!(resp.meta.total, 3);
794795
} else {
795796
assert_that!(resp.crates, empty());
797+
assert_eq!(resp.meta.total, 0);
796798
}
797799

798800
assert_eq!(resp.meta.prev_page, None);
799-
assert_eq!(resp.meta.total, 3);
800801
}
801802

802803
assert_eq!(calls, 4);

0 commit comments

Comments
 (0)