diff --git a/src/controllers/krate/search.rs b/src/controllers/krate/search.rs index 0e0aebee667..318e8239e2a 100644 --- a/src/controllers/krate/search.rs +++ b/src/controllers/krate/search.rs @@ -1,5 +1,6 @@ //! Endpoint for searching and discovery functionality +use diesel::sql_types::{NotNull, Nullable}; use diesel_full_text_search::*; use crate::controllers::helpers::Paginate; @@ -41,17 +42,20 @@ pub fn search(req: &mut dyn Request) -> CargoResult { .get("sort") .map(|s| &**s) .unwrap_or("recent-downloads"); + let mut has_filter = false; + let selection = ( + ALL_COLUMNS, + false.into_sql::(), + recent_crate_downloads::downloads.nullable(), + ); let mut query = crates::table .left_join(recent_crate_downloads::table) - .select(( - ALL_COLUMNS, - false.into_sql::(), - recent_crate_downloads::downloads.nullable(), - )) + .select(selection) .into_boxed(); if let Some(q_string) = params.get("q") { + has_filter = true; if !q_string.is_empty() { let sort = params.get("sort").map(|s| &**s).unwrap_or("relevance"); let q = plainto_tsquery(q_string); @@ -75,6 +79,7 @@ pub fn search(req: &mut dyn Request) -> CargoResult { } if let Some(cat) = params.get("category") { + has_filter = true; query = query.filter( crates::id.eq_any( crates_categories::table @@ -90,6 +95,7 @@ pub fn search(req: &mut dyn Request) -> CargoResult { } if let Some(kw) = params.get("keyword") { + has_filter = true; query = query.filter( crates::id.eq_any( crates_keywords::table @@ -99,6 +105,7 @@ pub fn search(req: &mut dyn Request) -> CargoResult { ), ); } else if let Some(letter) = params.get("letter") { + has_filter = true; let pattern = format!( "{}%", letter @@ -110,6 +117,7 @@ pub fn search(req: &mut dyn Request) -> CargoResult { ); query = query.filter(canon_crate_name(crates::name).like(pattern)); } else if let Some(user_id) = params.get("user_id").and_then(|s| s.parse::().ok()) { + has_filter = true; query = query.filter( crates::id.eq_any( crate_owners::table @@ -120,6 +128,7 @@ pub fn search(req: &mut dyn Request) -> CargoResult { ), ); } else if let Some(team_id) = params.get("team_id").and_then(|s| s.parse::().ok()) { + has_filter = true; query = query.filter( crates::id.eq_any( crate_owners::table @@ -130,6 +139,7 @@ pub fn search(req: &mut dyn Request) -> CargoResult { ), ); } else if params.get("following").is_some() { + has_filter = true; query = query.filter( crates::id.eq_any( follows::table @@ -151,9 +161,23 @@ pub fn search(req: &mut dyn Request) -> CargoResult { // The database query returns a tuple within a tuple, with the root // tuple containing 3 items. - let data = query - .paginate(limit, offset) - .load::<((Crate, bool, Option), i64)>(&*conn)?; + let data = if has_filter { + query + .paginate(limit, offset) + .load::<((Crate, bool, Option), i64)>(&*conn)? + } else { + sql_function!(fn coalesce(value: Nullable, default: T) -> T); + query + .select(( + // FIXME: Use `query.selection()` if that feature ends up in + // Diesel 2.0 + selection, + coalesce(crates::table.count().single_value(), 0), + )) + .limit(limit) + .offset(offset) + .load(&*conn)? + }; let total = data.first().map(|&(_, t)| t).unwrap_or(0); let perfect_matches = data.iter().map(|&((_, b, _), _)| b).collect::>(); let recent_downloads = data