diff --git a/app/controllers/application.js b/app/controllers/application.js index 8d8216d7424..b4646c22bf0 100644 --- a/app/controllers/application.js +++ b/app/controllers/application.js @@ -28,6 +28,7 @@ export default Controller.extend(EKMixin, { queryParams: { q: this.searchQuery, page: 1, + all_keywords: null, }, }); }, diff --git a/app/controllers/search.js b/app/controllers/search.js index 4c08b873454..a9be8a51dea 100644 --- a/app/controllers/search.js +++ b/app/controllers/search.js @@ -9,7 +9,7 @@ import PaginationMixin from '../mixins/pagination'; export default Controller.extend(PaginationMixin, { search: service(), - queryParams: ['q', 'page', 'per_page', 'sort'], + queryParams: ['all_keywords', 'page', 'per_page', 'q', 'sort'], q: alias('search.q'), page: '1', per_page: 10, diff --git a/app/routes/search.js b/app/routes/search.js index 62a8325e11d..cbd74a0da6d 100644 --- a/app/routes/search.js +++ b/app/routes/search.js @@ -2,8 +2,9 @@ import Route from '@ember/routing/route'; export default Route.extend({ queryParams: { - q: { refreshModel: true }, + all_keywords: { refreshModel: true }, page: { refreshModel: true }, + q: { refreshModel: true }, sort: { refreshModel: true }, }, diff --git a/migrations/2019-06-14-175833_change_keywords_to_text/down.sql b/migrations/2019-06-14-175833_change_keywords_to_text/down.sql new file mode 100644 index 00000000000..2acad5c25ab --- /dev/null +++ b/migrations/2019-06-14-175833_change_keywords_to_text/down.sql @@ -0,0 +1 @@ +ALTER TABLE keywords ALTER COLUMN keyword TYPE varchar; diff --git a/migrations/2019-06-14-175833_change_keywords_to_text/up.sql b/migrations/2019-06-14-175833_change_keywords_to_text/up.sql new file mode 100644 index 00000000000..b21d7a2e467 --- /dev/null +++ b/migrations/2019-06-14-175833_change_keywords_to_text/up.sql @@ -0,0 +1 @@ +ALTER TABLE keywords ALTER COLUMN keyword TYPE text; diff --git a/src/controllers/krate/search.rs b/src/controllers/krate/search.rs index d2c100e451b..2b71702e880 100644 --- a/src/controllers/krate/search.rs +++ b/src/controllers/krate/search.rs @@ -95,7 +95,28 @@ pub fn search(req: &mut dyn Request) -> CargoResult { ); } - if let Some(kw) = params.get("keyword") { + if let Some(kws) = params.get("all_keywords") { + use diesel::sql_types::Array; + sql_function!(#[aggregate] fn array_agg(x: T) -> Array); + + let names: Vec<_> = kws + .split_whitespace() + .map(|name| name.to_lowercase()) + .collect(); + + query = query.filter( + // FIXME: Just use `.contains` in Diesel 2.0 + // https://github.com/diesel-rs/diesel/issues/2066 + Contains::new( + crates_keywords::table + .inner_join(keywords::table) + .filter(crates_keywords::crate_id.eq(crates::id)) + .select(array_agg(keywords::keyword)) + .single_value(), + names.into_sql::>(), + ), + ); + } else if let Some(kw) = params.get("keyword") { query = query.filter( crates::id.eq_any( crates_keywords::table @@ -230,3 +251,5 @@ pub fn search(req: &mut dyn Request) -> CargoResult { }, })) } + +diesel_infix_operator!(Contains, "@>"); diff --git a/src/schema.rs b/src/schema.rs index 6b692eeada9..e1f3b593b72 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -544,10 +544,10 @@ table! { id -> Int4, /// The `keyword` column of the `keywords` table. /// - /// Its SQL type is `Varchar`. + /// Its SQL type is `Text`. /// /// (Automatically generated by Diesel.) - keyword -> Varchar, + keyword -> Text, /// The `crates_cnt` column of the `keywords` table. /// /// Its SQL type is `Int4`. diff --git a/src/tests/krate.rs b/src/tests/krate.rs index accaa6579c2..7a7563b441f 100644 --- a/src/tests/krate.rs +++ b/src/tests/krate.rs @@ -117,6 +117,11 @@ fn index_queries() { CrateBuilder::new("foo", user.id) .keyword("kw3") .expect_build(conn); + + CrateBuilder::new("two-keywords", user.id) + .keyword("kw1") + .keyword("kw3") + .expect_build(conn); (krate, krate2) }); @@ -124,11 +129,11 @@ fn index_queries() { // All of these fields should be indexed/searched by the queries assert_eq!(anon.search("q=foo").meta.total, 2); - assert_eq!(anon.search("q=kw1").meta.total, 2); + assert_eq!(anon.search("q=kw1").meta.total, 3); assert_eq!(anon.search("q=readme").meta.total, 1); assert_eq!(anon.search("q=description").meta.total, 1); - assert_eq!(anon.search_by_user_id(user.id).crates.len(), 3); + assert_eq!(anon.search_by_user_id(user.id).crates.len(), 4); assert_eq!(anon.search_by_user_id(0).crates.len(), 0); assert_eq!(anon.search("letter=F").crates.len(), 2); @@ -136,9 +141,10 @@ fn index_queries() { assert_eq!(anon.search("letter=b").crates.len(), 1); assert_eq!(anon.search("letter=c").crates.len(), 0); - assert_eq!(anon.search("keyword=kw1").crates.len(), 2); - assert_eq!(anon.search("keyword=KW1").crates.len(), 2); + assert_eq!(anon.search("keyword=kw1").crates.len(), 3); + assert_eq!(anon.search("keyword=KW1").crates.len(), 3); assert_eq!(anon.search("keyword=kw2").crates.len(), 0); + assert_eq!(anon.search("all_keywords=kw1 kw3").crates.len(), 1); assert_eq!(anon.search("q=foo&keyword=kw1").crates.len(), 1); assert_eq!(anon.search("q=foo2&keyword=kw1").crates.len(), 0);