Skip to content

Commit 0324b64

Browse files
committed
krate/search: improve performance with count subquery
1 parent af8a315 commit 0324b64

File tree

2 files changed

+71
-1
lines changed

2 files changed

+71
-1
lines changed

src/controllers/helpers/pagination.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,18 @@ pub(crate) trait Paginate: Sized {
146146
options,
147147
}
148148
}
149+
150+
fn pages_pagination_with_count_query<C>(
151+
self,
152+
options: PaginationOptions,
153+
count_query: C,
154+
) -> PaginatedQueryWithCountSubq<Self, C> {
155+
PaginatedQueryWithCountSubq {
156+
query: self,
157+
count_query,
158+
options,
159+
}
160+
}
149161
}
150162

151163
impl<T> Paginate for T {}
@@ -303,6 +315,61 @@ pub(crate) fn decode_seek<D: for<'a> Deserialize<'a>>(seek: &str) -> anyhow::Res
303315
Ok(decoded)
304316
}
305317

318+
#[derive(Debug)]
319+
pub(crate) struct PaginatedQueryWithCountSubq<T, C> {
320+
query: T,
321+
count_query: C,
322+
options: PaginationOptions,
323+
}
324+
325+
impl<T, C> QueryId for PaginatedQueryWithCountSubq<T, C> {
326+
const HAS_STATIC_QUERY_ID: bool = false;
327+
type QueryId = ();
328+
}
329+
330+
impl<
331+
T: Query,
332+
C: Query + QueryDsl + diesel::query_dsl::methods::SelectDsl<diesel::dsl::CountStar>,
333+
> Query for PaginatedQueryWithCountSubq<T, C>
334+
{
335+
type SqlType = (T::SqlType, BigInt);
336+
}
337+
338+
impl<T, C, DB> RunQueryDsl<DB> for PaginatedQueryWithCountSubq<T, C> {}
339+
340+
impl<T, C> QueryFragment<Pg> for PaginatedQueryWithCountSubq<T, C>
341+
where
342+
T: QueryFragment<Pg>,
343+
C: QueryFragment<Pg>,
344+
{
345+
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> {
346+
out.push_sql("SELECT *, (");
347+
self.count_query.walk_ast(out.reborrow())?;
348+
out.push_sql(") FROM (");
349+
self.query.walk_ast(out.reborrow())?;
350+
out.push_sql(") t LIMIT ");
351+
out.push_bind_param::<BigInt, _>(&self.options.per_page)?;
352+
if let Some(offset) = self.options.offset() {
353+
out.push_sql(format!(" OFFSET {offset}").as_str());
354+
}
355+
Ok(())
356+
}
357+
}
358+
359+
impl<T, C> PaginatedQueryWithCountSubq<T, C> {
360+
pub(crate) fn load<'a, U>(self, conn: &mut PgConnection) -> QueryResult<Paginated<U>>
361+
where
362+
Self: LoadQuery<'a, PgConnection, WithCount<U>>,
363+
{
364+
let options = self.options.clone();
365+
let records_and_total = self.internal_load(conn)?.collect::<QueryResult<_>>()?;
366+
Ok(Paginated {
367+
records_and_total,
368+
options,
369+
})
370+
}
371+
}
372+
306373
#[cfg(test)]
307374
mod tests {
308375
use super::*;

src/controllers/krate/search.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,10 @@ pub async fn search(app: AppState, req: Parts) -> AppResult<Json<Value>> {
186186

187187
(total, next_page, None, results, conn)
188188
} else {
189-
let query = query.pages_pagination(pagination);
189+
let query = query.pages_pagination_with_count_query(
190+
pagination,
191+
filter_params.make_query(&req, conn)?.count(),
192+
);
190193
let data: Paginated<(Crate, bool, Option<i64>)> =
191194
info_span!("db.query", message = "SELECT ..., COUNT(*) FROM crates")
192195
.in_scope(|| query.load(conn))?;

0 commit comments

Comments
 (0)