Skip to content

Commit 20a589e

Browse files
committed
Auto merge of #1543 - pwoolcoc:allow-multiple-keywords-in-search, r=jtgeibel
Allow multiple keywords in crate search This PR allows URLs like crates.io/search?all_keywords=gotham+middleware to enable linking to a search that returns crates that have all keywords specified in the parameter. Towards #1461
2 parents 5a08887 + 9a3485b commit 20a589e

File tree

8 files changed

+42
-9
lines changed

8 files changed

+42
-9
lines changed

app/controllers/application.js

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export default Controller.extend(EKMixin, {
2828
queryParams: {
2929
q: this.searchQuery,
3030
page: 1,
31+
all_keywords: null,
3132
},
3233
});
3334
},

app/controllers/search.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import PaginationMixin from '../mixins/pagination';
99

1010
export default Controller.extend(PaginationMixin, {
1111
search: service(),
12-
queryParams: ['q', 'page', 'per_page', 'sort'],
12+
queryParams: ['all_keywords', 'page', 'per_page', 'q', 'sort'],
1313
q: alias('search.q'),
1414
page: '1',
1515
per_page: 10,

app/routes/search.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import Route from '@ember/routing/route';
22

33
export default Route.extend({
44
queryParams: {
5-
q: { refreshModel: true },
5+
all_keywords: { refreshModel: true },
66
page: { refreshModel: true },
7+
q: { refreshModel: true },
78
sort: { refreshModel: true },
89
},
910

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE keywords ALTER COLUMN keyword TYPE varchar;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE keywords ALTER COLUMN keyword TYPE text;

src/controllers/krate/search.rs

+24-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,28 @@ pub fn search(req: &mut dyn Request) -> AppResult<Response> {
9696
);
9797
}
9898

99-
if let Some(kw) = params.get("keyword") {
99+
if let Some(kws) = params.get("all_keywords") {
100+
use diesel::sql_types::Array;
101+
sql_function!(#[aggregate] fn array_agg<T>(x: T) -> Array<T>);
102+
103+
let names: Vec<_> = kws
104+
.split_whitespace()
105+
.map(|name| name.to_lowercase())
106+
.collect();
107+
108+
query = query.filter(
109+
// FIXME: Just use `.contains` in Diesel 2.0
110+
// https://github.com/diesel-rs/diesel/issues/2066
111+
Contains::new(
112+
crates_keywords::table
113+
.inner_join(keywords::table)
114+
.filter(crates_keywords::crate_id.eq(crates::id))
115+
.select(array_agg(keywords::keyword))
116+
.single_value(),
117+
names.into_sql::<Array<Text>>(),
118+
),
119+
);
120+
} else if let Some(kw) = params.get("keyword") {
100121
query = query.filter(
101122
crates::id.eq_any(
102123
crates_keywords::table
@@ -227,3 +248,5 @@ pub fn search(req: &mut dyn Request) -> AppResult<Response> {
227248
},
228249
}))
229250
}
251+
252+
diesel_infix_operator!(Contains, "@>");

src/schema.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -550,10 +550,10 @@ table! {
550550
id -> Int4,
551551
/// The `keyword` column of the `keywords` table.
552552
///
553-
/// Its SQL type is `Varchar`.
553+
/// Its SQL type is `Text`.
554554
///
555555
/// (Automatically generated by Diesel.)
556-
keyword -> Varchar,
556+
keyword -> Text,
557557
/// The `crates_cnt` column of the `keywords` table.
558558
///
559559
/// Its SQL type is `Int4`.

src/tests/krate.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -117,28 +117,34 @@ fn index_queries() {
117117
CrateBuilder::new("foo", user.id)
118118
.keyword("kw3")
119119
.expect_build(conn);
120+
121+
CrateBuilder::new("two-keywords", user.id)
122+
.keyword("kw1")
123+
.keyword("kw3")
124+
.expect_build(conn);
120125
(krate, krate2)
121126
});
122127

123128
assert_eq!(anon.search("q=baz").meta.total, 0);
124129

125130
// All of these fields should be indexed/searched by the queries
126131
assert_eq!(anon.search("q=foo").meta.total, 2);
127-
assert_eq!(anon.search("q=kw1").meta.total, 2);
132+
assert_eq!(anon.search("q=kw1").meta.total, 3);
128133
assert_eq!(anon.search("q=readme").meta.total, 1);
129134
assert_eq!(anon.search("q=description").meta.total, 1);
130135

131-
assert_eq!(anon.search_by_user_id(user.id).crates.len(), 3);
136+
assert_eq!(anon.search_by_user_id(user.id).crates.len(), 4);
132137
assert_eq!(anon.search_by_user_id(0).crates.len(), 0);
133138

134139
assert_eq!(anon.search("letter=F").crates.len(), 2);
135140
assert_eq!(anon.search("letter=B").crates.len(), 1);
136141
assert_eq!(anon.search("letter=b").crates.len(), 1);
137142
assert_eq!(anon.search("letter=c").crates.len(), 0);
138143

139-
assert_eq!(anon.search("keyword=kw1").crates.len(), 2);
140-
assert_eq!(anon.search("keyword=KW1").crates.len(), 2);
144+
assert_eq!(anon.search("keyword=kw1").crates.len(), 3);
145+
assert_eq!(anon.search("keyword=KW1").crates.len(), 3);
141146
assert_eq!(anon.search("keyword=kw2").crates.len(), 0);
147+
assert_eq!(anon.search("all_keywords=kw1 kw3").crates.len(), 1);
142148

143149
assert_eq!(anon.search("q=foo&keyword=kw1").crates.len(), 1);
144150
assert_eq!(anon.search("q=foo2&keyword=kw1").crates.len(), 0);

0 commit comments

Comments
 (0)