Skip to content

Commit 2b78c36

Browse files
authored
Merge pull request #10021 from Turbo87/async-pagination
Implement async pagination
2 parents ed30350 + fb99e47 commit 2b78c36

File tree

4 files changed

+297
-297
lines changed

4 files changed

+297
-297
lines changed

src/controllers/helpers/pagination.rs

+45-20
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ use crate::models::helpers::with_count::*;
77
use crate::util::errors::{bad_request, AppResult};
88
use crate::util::{HeaderMapExt, RequestUtils};
99

10-
use crate::util::diesel::prelude::*;
1110
use base64::{engine::general_purpose, Engine};
1211
use diesel::pg::Pg;
12+
use diesel::prelude::*;
1313
use diesel::query_builder::{AstPass, Query, QueryFragment, QueryId};
14-
use diesel::query_dsl::LoadQuery;
1514
use diesel::sql_types::BigInt;
15+
use diesel_async::AsyncPgConnection;
16+
use futures_util::future::BoxFuture;
17+
use futures_util::{FutureExt, TryStreamExt};
1618
use http::header;
1719
use indexmap::IndexMap;
1820
use serde::{Deserialize, Serialize};
@@ -250,16 +252,29 @@ pub(crate) struct PaginatedQuery<T> {
250252
}
251253

252254
impl<T> PaginatedQuery<T> {
253-
pub(crate) fn load<'a, U, Conn>(self, conn: &mut Conn) -> QueryResult<Paginated<U>>
255+
pub fn load<'a, U>(
256+
self,
257+
conn: &'a mut AsyncPgConnection,
258+
) -> BoxFuture<'a, QueryResult<Paginated<U>>>
254259
where
255-
Self: LoadQuery<'a, Conn, WithCount<U>>,
260+
Self: diesel_async::methods::LoadQuery<'a, AsyncPgConnection, WithCount<U>>,
261+
T: 'a,
262+
U: Send + 'a,
256263
{
264+
use diesel_async::methods::LoadQuery;
265+
257266
let options = self.options.clone();
258-
let records_and_total = self.internal_load(conn)?.collect::<QueryResult<_>>()?;
259-
Ok(Paginated {
260-
records_and_total,
261-
options,
262-
})
267+
let future = self.internal_load(conn);
268+
269+
async move {
270+
let records_and_total = future.await?.try_collect().await?;
271+
272+
Ok(Paginated {
273+
records_and_total,
274+
options,
275+
})
276+
}
277+
.boxed()
263278
}
264279
}
265280

@@ -272,8 +287,6 @@ impl<T: Query> Query for PaginatedQuery<T> {
272287
type SqlType = (T::SqlType, BigInt);
273288
}
274289

275-
impl<T, DB> diesel::RunQueryDsl<DB> for PaginatedQuery<T> {}
276-
277290
impl<T> QueryFragment<Pg> for PaginatedQuery<T>
278291
where
279292
T: QueryFragment<Pg>,
@@ -366,8 +379,6 @@ impl<
366379
type SqlType = (T::SqlType, BigInt);
367380
}
368381

369-
impl<T, C, DB> diesel::RunQueryDsl<DB> for PaginatedQueryWithCountSubq<T, C> {}
370-
371382
impl<T, C> QueryFragment<Pg> for PaginatedQueryWithCountSubq<T, C>
372383
where
373384
T: QueryFragment<Pg>,
@@ -390,16 +401,30 @@ where
390401
}
391402

392403
impl<T, C> PaginatedQueryWithCountSubq<T, C> {
393-
pub(crate) fn load<'a, U, Conn>(self, conn: &mut Conn) -> QueryResult<Paginated<U>>
404+
pub fn load<'a, U>(
405+
self,
406+
conn: &'a mut AsyncPgConnection,
407+
) -> BoxFuture<'a, QueryResult<Paginated<U>>>
394408
where
395-
Self: LoadQuery<'a, Conn, WithCount<U>>,
409+
Self: diesel_async::methods::LoadQuery<'a, AsyncPgConnection, WithCount<U>> + Send,
410+
C: 'a,
411+
T: 'a,
412+
U: Send + 'a,
396413
{
414+
use diesel_async::methods::LoadQuery;
415+
397416
let options = self.options.clone();
398-
let records_and_total = self.internal_load(conn)?.collect::<QueryResult<_>>()?;
399-
Ok(Paginated {
400-
records_and_total,
401-
options,
402-
})
417+
let future = self.internal_load(conn);
418+
419+
async move {
420+
let records_and_total = future.await?.try_collect().await?;
421+
422+
Ok(Paginated {
423+
records_and_total,
424+
options,
425+
})
426+
}
427+
.boxed()
403428
}
404429
}
405430

src/controllers/keyword.rs

+12-19
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@ use crate::app::AppState;
22
use crate::controllers::helpers::pagination::PaginationOptions;
33
use crate::controllers::helpers::{pagination::Paginated, Paginate};
44
use crate::models::Keyword;
5-
use crate::tasks::spawn_blocking;
65
use crate::util::errors::AppResult;
76
use crate::views::EncodableKeyword;
87
use axum::extract::{Path, Query};
98
use axum_extra::json;
109
use axum_extra::response::ErasedJson;
1110
use diesel::prelude::*;
12-
use diesel_async::async_connection_wrapper::AsyncConnectionWrapper;
1311
use http::request::Parts;
1412

1513
#[derive(Deserialize)]
@@ -30,23 +28,18 @@ pub async fn index(state: AppState, qp: Query<IndexQuery>, req: Parts) -> AppRes
3028

3129
let query = query.pages_pagination(PaginationOptions::builder().gather(&req)?);
3230

33-
let conn = state.db_read().await?;
34-
spawn_blocking(move || {
35-
let conn: &mut AsyncConnectionWrapper<_> = &mut conn.into();
36-
37-
let data: Paginated<Keyword> = query.load(conn)?;
38-
let total = data.total();
39-
let kws = data
40-
.into_iter()
41-
.map(Keyword::into)
42-
.collect::<Vec<EncodableKeyword>>();
43-
44-
Ok(json!({
45-
"keywords": kws,
46-
"meta": { "total": total },
47-
}))
48-
})
49-
.await?
31+
let mut conn = state.db_read().await?;
32+
let data: Paginated<Keyword> = query.load(&mut conn).await?;
33+
let total = data.total();
34+
let kws = data
35+
.into_iter()
36+
.map(Keyword::into)
37+
.collect::<Vec<EncodableKeyword>>();
38+
39+
Ok(json!({
40+
"keywords": kws,
41+
"meta": { "total": total },
42+
}))
5043
}
5144

5245
/// Handles the `GET /keywords/:keyword_id` route.

0 commit comments

Comments
 (0)