Skip to content

Commit 5a1a85d

Browse files
author
rfm
committed
Merge branch 'pull_1560'
2 parents 41af25d + 15bc5d1 commit 5a1a85d

File tree

3 files changed

+90
-1
lines changed

3 files changed

+90
-1
lines changed

src/controllers/krate/search.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ pub fn search(req: &mut dyn Request) -> CargoResult<Response> {
3737
let conn = req.db_conn()?;
3838
let (offset, limit) = req.pagination(10, 100)?;
3939
let params = req.query();
40+
//extract the search param for loose searching
41+
let search_q = if let Some(q) = params.get("q") {
42+
format!("%{}%", q)
43+
} else {
44+
String::new()
45+
};
4046
let sort = params
4147
.get("sort")
4248
.map(|s| &**s)
@@ -57,7 +63,7 @@ pub fn search(req: &mut dyn Request) -> CargoResult<Response> {
5763
let q = plainto_tsquery(q_string);
5864
query = query.filter(
5965
q.matches(crates::textsearchable_index_col)
60-
.or(Crate::with_name(q_string)),
66+
.or(Crate::like_name(&search_q)),
6167
);
6268

6369
query = query.select((

src/models/krate.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ pub const MAX_NAME_LENGTH: usize = 64;
8787
type CanonCrateName<T> = self::canon_crate_name::HelperType<T>;
8888
type All = diesel::dsl::Select<crates::table, AllColumns>;
8989
type WithName<'a> = diesel::dsl::Eq<CanonCrateName<crates::name>, CanonCrateName<&'a str>>;
90+
/// The result of a loose search
91+
type LikeName<'a> = diesel::pg::expression::helper_types::ILike<
92+
CanonCrateName<crates::name>,
93+
CanonCrateName<&'a str>,
94+
>;
9095
type ByName<'a> = diesel::dsl::Filter<All, WithName<'a>>;
9196
type ByExactName<'a> = diesel::dsl::Filter<All, diesel::dsl::Eq<crates::name, &'a str>>;
9297

@@ -235,6 +240,21 @@ impl<'a> NewCrate<'a> {
235240
}
236241

237242
impl Crate {
243+
/// SQL filter with the `like` binary operator
244+
/// ```sql
245+
/// SELECT *
246+
/// FROM crates
247+
/// WHERE name like $1
248+
/// ```
249+
pub fn like_name(name: &str) -> LikeName<'_> {
250+
canon_crate_name(crates::name).ilike(canon_crate_name(name))
251+
}
252+
/// SQL filter with the = binary operator
253+
/// ```sql
254+
/// SELECT *
255+
/// FROM crates
256+
/// WHERE name = $1
257+
/// ```
238258
pub fn with_name(name: &str) -> WithName<'_> {
239259
canon_crate_name(crates::name).eq(canon_crate_name(name))
240260
}

src/tests/krate.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,69 @@ fn exact_match_on_queries_with_sort() {
414414
assert_eq!(json.crates[3].name, "other_sort");
415415
}
416416

417+
#[test]
418+
fn loose_search_order() {
419+
let (app, anon, user) = TestApp::init().with_user();
420+
let user = user.as_model();
421+
422+
let ordered = app.db(|conn| {
423+
// exact match should be first
424+
let one = CrateBuilder::new("temp", user.id)
425+
.readme("readme")
426+
.description("description")
427+
.keyword("kw1")
428+
.expect_build(conn);
429+
// file shouldn't match at all
430+
CrateBuilder::new("file", user.id)
431+
.readme("readme")
432+
.description("description")
433+
.keyword("kw1")
434+
.expect_build(conn);
435+
// temp_udp should match second
436+
let two = CrateBuilder::new("temp_utp", user.id)
437+
.readme("readme")
438+
.description("description")
439+
.keyword("kw1")
440+
.expect_build(conn);
441+
// evalrs should match 3rd because of readme
442+
let three = CrateBuilder::new("evalrs", user.id)
443+
.readme(
444+
r#"$ echo 'println!("Hello World!")' | evalrs
445+
Compiling evalrs_temp v0.0.0 (file:///tmp/evalrs_temp.daiPxHtjV2VR)
446+
Finished debug [unoptimized + debuginfo] target(s) in 0.51 secs
447+
Running `target\debug\evalrs_temp.exe`
448+
Hello World!"#,
449+
)
450+
.description("description")
451+
.keyword("kw1")
452+
.expect_build(conn);
453+
// tempfile should appear 4th
454+
let four = CrateBuilder::new("tempfile", user.id)
455+
.readme("readme")
456+
.description("description")
457+
.keyword("kw1")
458+
.expect_build(conn);
459+
// mkstemp should appear 5th
460+
let five = CrateBuilder::new("mkstemp", user.id)
461+
.readme("readme")
462+
.description("description")
463+
.keyword("kw1")
464+
.expect_build(conn);
465+
vec![one, two, three, four, five]
466+
});
467+
let search_temp = anon.search("q=temp");
468+
assert_eq!(search_temp.meta.total, 5);
469+
assert_eq!(search_temp.crates.len(), 5);
470+
for (lhs, rhs) in search_temp.crates.iter().zip(ordered) {
471+
assert_eq!(lhs.name, rhs.name);
472+
}
473+
let search_file = anon.search("q=file");
474+
assert_eq!(search_file.meta.total, 2);
475+
assert_eq!(search_file.crates.len(), 2);
476+
assert_eq!(&search_file.crates[0].name, "file");
477+
assert_eq!(&search_file.crates[1].name, "tempfile");
478+
}
479+
417480
#[test]
418481
fn show() {
419482
let (app, anon, user) = TestApp::init().with_user();

0 commit comments

Comments
 (0)