Skip to content

Commit 23d0863

Browse files
committed
Normalise keywords to lowercase.
Currently keywords are case-sensitive, but this is often unhelpful, as they are heuristic classification tools, not hard guarantees. E.g. ["fft"](https://crates.io/keywords/fft) contains different crates to ["FFT"](https://crates.io/keywords/FFT). This patch normalises keywords to lowercase as they go into the database. Closes #100.
1 parent 0e82004 commit 23d0863

File tree

4 files changed

+22
-5
lines changed

4 files changed

+22
-5
lines changed

src/bin/migrate.rs

100644100755
+5
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,11 @@ fn migrations() -> Vec<Migration> {
418418
crate_owners_unique_user_per_crate", &[]));
419419
Ok(())
420420
}),
421+
Migration::new(20150320174400, |tx| {
422+
try!(tx.execute("ALTER TABLE keyword SET keyword = lower(keyword)",
423+
&[]));
424+
Ok(())
425+
}, |_| Ok(())),
421426
];
422427
// NOTE: Generate a new id via `date +"%Y%m%d%H%M%S"`
423428

src/keyword.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ impl Keyword {
3636
pub fn find_by_keyword(conn: &Connection, name: &str)
3737
-> CargoResult<Option<Keyword>> {
3838
let stmt = try!(conn.prepare("SELECT * FROM keywords \
39-
WHERE keyword = $1"));
39+
WHERE keyword = lower($1)"));
4040
let rows = try!(stmt.query(&[&name as &ToSql]));
4141
Ok(rows.iter().next().map(|r| Model::from_row(&r)))
4242
}
@@ -45,13 +45,13 @@ impl Keyword {
4545
-> CargoResult<Keyword> {
4646
// TODO: racy (the select then insert is not atomic)
4747
let stmt = try!(conn.prepare("SELECT * FROM keywords
48-
WHERE keyword = $1"));
48+
WHERE keyword = lower($1)"));
4949
for row in try!(stmt.query(&[&name as &ToSql])) {
5050
return Ok(Model::from_row(&row))
5151
}
5252

5353
let stmt = try!(conn.prepare("INSERT INTO keywords \
54-
(keyword, created_at, crates_cnt)
54+
(lower(keyword), created_at, crates_cnt)
5555
VALUES ($1, $2, 0) \
5656
RETURNING *"));
5757
let now = ::now();
@@ -185,4 +185,3 @@ pub fn show(req: &mut Request) -> CargoResult<Response> {
185185
struct R { keyword: EncodableKeyword }
186186
Ok(req.json(&R { keyword: kw.encodable() }))
187187
}
188-

src/krate.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ pub fn index(req: &mut Request) -> CargoResult<Response> {
460460
ON crates.id = crates_keywords.crate_id
461461
INNER JOIN keywords
462462
ON crates_keywords.keyword_id = keywords.id
463-
WHERE keywords.keyword = $1";
463+
WHERE keywords.keyword = lower($1)";
464464
(format!("SELECT crates.* {} {} LIMIT $2 OFFSET $3", base, sort_sql),
465465
format!("SELECT COUNT(crates.*) {}", base))
466466
})

src/tests/keyword.rs

+13
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@ fn show() {
4343
assert_eq!(json.keyword.keyword, "foo".to_string());
4444
}
4545

46+
#[test]
47+
fn show_case_insensitive() {
48+
let (_b, app, middle) = ::app();
49+
let mut req = ::req(app, Method::Get, "/api/v1/keywords/fOO");
50+
let response = t_resp!(middle.call(&mut req));
51+
assert_eq!(response.status.0, 404);
52+
53+
::mock_keyword(&mut req, "Foo");
54+
let mut response = ok_resp!(middle.call(&mut req));
55+
let json: GoodKeyword = ::json(&mut response);
56+
assert_eq!(json.keyword.keyword, "foo".to_string());
57+
}
58+
4659
fn tx(req: &Request) -> &Connection { req.tx().unwrap() }
4760

4861
#[test]

0 commit comments

Comments
 (0)