Skip to content

Commit e41e388

Browse files
Merge pull request #751 from sgrif/sg-paginate-trait
Refactor paginating queries to a trait
2 parents 1dd1c9d + 6d7ab2e commit e41e388

File tree

5 files changed

+66
-38
lines changed

5 files changed

+66
-38
lines changed

src/keyword.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use pg::rows::Row;
1010

1111
use {Model, Crate};
1212
use db::RequestTransaction;
13+
use pagination::Paginate;
1314
use schema::*;
1415
use util::{RequestUtils, CargoResult};
1516

@@ -138,28 +139,22 @@ impl Model for Keyword {
138139

139140
/// Handles the `GET /keywords` route.
140141
pub fn index(req: &mut Request) -> CargoResult<Response> {
141-
use diesel::expression::dsl::sql;
142-
use diesel::types::BigInt;
143142
use schema::keywords;
144143

145144
let conn = req.db_conn()?;
146145
let (offset, limit) = req.pagination(10, 100)?;
147146
let query = req.query();
148147
let sort = query.get("sort").map(|s| &s[..]).unwrap_or("alpha");
149148

150-
let mut query = keywords::table
151-
.select((keywords::all_columns, sql::<BigInt>("COUNT(*) OVER ()")))
152-
.limit(limit)
153-
.offset(offset)
154-
.into_boxed();
149+
let mut query = keywords::table.into_boxed();
155150

156151
if sort == "crates" {
157152
query = query.order(keywords::crates_cnt.desc());
158153
} else {
159154
query = query.order(keywords::keyword.asc());
160155
}
161156

162-
let data = query.load::<(Keyword, i64)>(&*conn)?;
157+
let data = query.paginate(limit, offset).load::<(Keyword, i64)>(&*conn)?;
163158
let total = data.get(0).map(|&(_, t)| t).unwrap_or(0);
164159
let kws = data.into_iter()
165160
.map(|(k, _)| k.encodable())

src/krate.rs

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use download::{VersionDownload, EncodableVersionDownload};
2929
use git;
3030
use keyword::{EncodableKeyword, CrateKeyword};
3131
use owner::{EncodableOwner, Owner, Rights, OwnerKind, Team, rights, CrateOwner};
32+
use pagination::Paginate;
3233
use schema::*;
3334
use upload;
3435
use user::RequestUser;
@@ -781,22 +782,16 @@ impl Model for Crate {
781782
/// Handles the `GET /crates` route.
782783
#[allow(trivial_casts)]
783784
pub fn index(req: &mut Request) -> CargoResult<Response> {
784-
use diesel::expression::dsl::sql;
785-
use diesel::types::{BigInt, Bool};
785+
use diesel::expression::AsExpression;
786+
use diesel::types::Bool;
786787

787788
let conn = req.db_conn()?;
788789
let (offset, limit) = req.pagination(10, 100)?;
789790
let params = req.query();
790791
let sort = params.get("sort").map(|s| &**s).unwrap_or("alpha");
791792

792793
let mut query = crates::table
793-
.select((
794-
ALL_COLUMNS,
795-
sql::<BigInt>("COUNT(*) OVER ()"),
796-
sql::<Bool>("false"),
797-
))
798-
.limit(limit)
799-
.offset(offset)
794+
.select((ALL_COLUMNS, AsExpression::<Bool>::as_expression(false)))
800795
.into_boxed();
801796

802797
if sort == "downloads" {
@@ -813,11 +808,7 @@ pub fn index(req: &mut Request) -> CargoResult<Response> {
813808
),
814809
));
815810

816-
query = query.select((
817-
ALL_COLUMNS,
818-
sql::<BigInt>("COUNT(*) OVER()"),
819-
crates::name.eq(q_string),
820-
));
811+
query = query.select((ALL_COLUMNS, crates::name.eq(q_string)));
821812
let perfect_match = crates::name.eq(q_string).desc();
822813
if sort == "downloads" {
823814
query = query.order((perfect_match, crates::downloads.desc()));
@@ -887,12 +878,14 @@ pub fn index(req: &mut Request) -> CargoResult<Response> {
887878
));
888879
}
889880

890-
let data = query.load::<(Crate, i64, bool)>(&*conn)?;
891-
let total = data.get(0).map(|&(_, t, _)| t).unwrap_or(0);
881+
let data = query.paginate(limit, offset).load::<((Crate, bool), i64)>(
882+
&*conn,
883+
)?;
884+
let total = data.first().map(|&(_, t)| t).unwrap_or(0);
892885
let crates = data.iter()
893-
.map(|&(ref c, _, _)| c.clone())
886+
.map(|&((ref c, _), _)| c.clone())
894887
.collect::<Vec<_>>();
895-
let perfect_matches = data.into_iter().map(|(_, _, b)| b).collect::<Vec<_>>();
888+
let perfect_matches = data.into_iter().map(|((_, b), _)| b).collect::<Vec<_>>();
896889

897890
let versions = Version::belonging_to(&crates)
898891
.load::<Version>(&*conn)?

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ pub mod user;
9292
pub mod util;
9393
pub mod version;
9494

95+
mod pagination;
96+
9597
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
9698
pub enum Env {
9799
Development,

src/pagination.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use diesel::prelude::*;
2+
use diesel::query_builder::*;
3+
use diesel::types::BigInt;
4+
use diesel::pg::Pg;
5+
6+
pub struct Paginated<T> {
7+
query: T,
8+
limit: i64,
9+
offset: i64,
10+
}
11+
12+
pub trait Paginate: AsQuery + Sized {
13+
fn paginate(self, limit: i64, offset: i64) -> Paginated<Self::Query> {
14+
Paginated {
15+
query: self.as_query(),
16+
limit,
17+
offset,
18+
}
19+
}
20+
}
21+
22+
impl<T: AsQuery> Paginate for T {}
23+
24+
impl<T: Query> Query for Paginated<T> {
25+
type SqlType = (T::SqlType, BigInt);
26+
}
27+
28+
impl<T> QueryFragment<Pg> for Paginated<T>
29+
where
30+
T: QueryFragment<Pg>,
31+
{
32+
fn walk_ast(&self, mut out: AstPass<Pg>) -> QueryResult<()> {
33+
out.push_sql("SELECT *, COUNT(*) OVER () FROM (");
34+
self.query.walk_ast(out.reborrow())?;
35+
out.push_sql(") t LIMIT ");
36+
out.push_bind_param::<BigInt, _>(&self.limit)?;
37+
out.push_sql(" OFFSET ");
38+
out.push_bind_param::<BigInt, _>(&self.offset)?;
39+
Ok(())
40+
}
41+
}
42+
43+
impl_query_id!(Paginated<T>);

src/user/mod.rs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::borrow::Cow;
1111
use app::RequestApp;
1212
use db::RequestTransaction;
1313
use krate::Follow;
14+
use pagination::Paginate;
1415
use schema::*;
1516
use util::errors::NotFound;
1617
use util::{RequestUtils, CargoResult, internal, ChainError, human};
@@ -377,8 +378,7 @@ pub fn show_team(req: &mut Request) -> CargoResult<Response> {
377378

378379
/// Handles the `GET /me/updates` route.
379380
pub fn updates(req: &mut Request) -> CargoResult<Response> {
380-
use diesel::expression::dsl::{any, sql};
381-
use diesel::types::BigInt;
381+
use diesel::expression::dsl::any;
382382

383383
let user = req.user()?;
384384
let (offset, limit) = req.pagination(10, 100)?;
@@ -389,21 +389,16 @@ pub fn updates(req: &mut Request) -> CargoResult<Response> {
389389
.inner_join(crates::table)
390390
.filter(crates::id.eq(any(followed_crates)))
391391
.order(versions::created_at.desc())
392-
.limit(limit)
393-
.offset(offset)
394-
.select((
395-
versions::all_columns,
396-
crates::name,
397-
sql::<BigInt>("COUNT(*) OVER ()"),
398-
))
399-
.load::<(Version, String, i64)>(&*conn)?;
392+
.select((versions::all_columns, crates::name))
393+
.paginate(limit, offset)
394+
.load::<((Version, String), i64)>(&*conn)?;
400395

401396
let more = data.get(0)
402-
.map(|&(_, _, count)| count > offset + limit)
397+
.map(|&(_, count)| count > offset + limit)
403398
.unwrap_or(false);
404399

405400
let versions = data.into_iter()
406-
.map(|(version, crate_name, _)| version.encodable(&crate_name))
401+
.map(|((version, crate_name), _)| version.encodable(&crate_name))
407402
.collect();
408403

409404
#[derive(RustcEncodable)]

0 commit comments

Comments
 (0)