diff --git a/src/controllers/krate/search.rs b/src/controllers/krate/search.rs index e61ba527646..8120b3e860b 100644 --- a/src/controllers/krate/search.rs +++ b/src/controllers/krate/search.rs @@ -66,7 +66,7 @@ pub fn search(req: &mut dyn Request) -> CargoResult { query = query.filter( q.clone() .matches(crates::textsearchable_index_col) - .or(Crate::like_name(&q_string)), + .or(Crate::loosly_matches_name(&q_string)), ); query = query.select(( diff --git a/src/models/krate.rs b/src/models/krate.rs index 376dcdb5a7d..7c16ef29392 100644 --- a/src/models/krate.rs +++ b/src/models/krate.rs @@ -2,6 +2,7 @@ use chrono::NaiveDateTime; use diesel::associations::Identifiable; use diesel::pg::Pg; use diesel::prelude::*; +use diesel::sql_types::Bool; use url::Url; use crate::app::App; @@ -86,8 +87,6 @@ pub const MAX_NAME_LENGTH: usize = 64; type CanonCrateName = self::canon_crate_name::HelperType; type All = diesel::dsl::Select; type WithName<'a> = diesel::dsl::Eq, CanonCrateName<&'a str>>; -/// The result of a loose search -type LikeName = diesel::dsl::Like, CanonCrateName>; type ByName<'a> = diesel::dsl::Filter>; type ByExactName<'a> = diesel::dsl::Filter>; @@ -236,12 +235,28 @@ impl<'a> NewCrate<'a> { } impl Crate { - /// SQL filter with the `like` binary operator. Adds wildcards to the beginning and end to get - /// substring matches. - pub fn like_name(name: &str) -> LikeName { - let wildcard_name = format!("%{}%", name); - canon_crate_name(crates::name).like(canon_crate_name(wildcard_name)) + /// SQL filter based on whether the crate's name loosly matches the given + /// string. + /// + /// The operator used varies based on the input. + pub fn loosly_matches_name( + name: &str, + ) -> Box + '_> + where + crates::name: SelectableExpression, + { + if name.len() > 2 { + let wildcard_name = format!("%{}%", name); + Box::new(canon_crate_name(crates::name).like(canon_crate_name(wildcard_name))) + } else { + diesel_infix_operator!(MatchesWord, "%>"); + Box::new(MatchesWord::new( + canon_crate_name(crates::name), + name.into_sql::(), + )) + } } + /// SQL filter with the = binary operator pub fn with_name(name: &str) -> WithName<'_> { canon_crate_name(crates::name).eq(canon_crate_name(name)) diff --git a/src/tests/krate.rs b/src/tests/krate.rs index 554e2410a3c..081b9bb11cf 100644 --- a/src/tests/krate.rs +++ b/src/tests/krate.rs @@ -452,6 +452,10 @@ fn loose_search_order() { for (lhs, rhs) in search_temp.crates.iter().zip(ordered) { assert_eq!(lhs.name, rhs.name); } + + let search_temp = anon.search("q=te"); + assert_eq!(search_temp.meta.total, 3); + assert_eq!(search_temp.crates.len(), 3); } #[test]