Skip to content

Commit 37ca7cd

Browse files
sypharJoshua Nelson
authored and
Joshua Nelson
committed
Fix I'm feeling lucky search to include all crates
1 parent c816c03 commit 37ca7cd

File tree

1 file changed

+77
-41
lines changed

1 file changed

+77
-41
lines changed

src/web/releases.rs

Lines changed: 77 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use crate::{
44
build_queue::QueuedCrate,
5-
db::Pool,
5+
db::{Pool, PoolClient},
66
impl_webpage,
77
web::{error::Nope, match_version, page::WebPage, redirect_base},
88
BuildQueue,
@@ -497,6 +497,69 @@ impl Default for Search {
497497
}
498498
}
499499

500+
fn redirect_to_random_crate(req: &Request, conn: &mut PoolClient) -> IronResult<Response> {
501+
// since there is a chance of this query returning an empty result,
502+
// we retry a certain amount of times, and log a warning.
503+
for _ in 0..20 {
504+
let rows = ctry!(
505+
req,
506+
conn.query(
507+
"WITH params AS (
508+
-- get maximum possible id-value in crates-table
509+
SELECT last_value AS max_id FROM crates_id_seq
510+
)
511+
SELECT
512+
crates.name,
513+
releases.version,
514+
releases.target_name
515+
FROM (
516+
-- generate 500 random numbers in the ID-range.
517+
-- this might have to be increased when we have to repeat
518+
-- this query too often.
519+
-- it depends on the percentage of crates with > 100 stars
520+
SELECT DISTINCT 1 + trunc(random() * params.max_id)::INTEGER AS id
521+
FROM params, generate_series(1, 500)
522+
) AS r
523+
INNER JOIN crates ON r.id = crates.id
524+
INNER JOIN releases ON crates.latest_version_id = releases.id
525+
INNER JOIN github_repos ON releases.github_repo = github_repos.id
526+
WHERE
527+
releases.rustdoc_status = TRUE AND
528+
github_repos.stars >= 100
529+
LIMIT 1
530+
",
531+
&[]
532+
),
533+
);
534+
535+
if let Some(row) = rows.into_iter().next() {
536+
let name: String = row.get("name");
537+
let version: String = row.get("version");
538+
let target_name: String = row.get("target_name");
539+
let url = ctry!(
540+
req,
541+
Url::parse(&format!(
542+
"{}/{}/{}/{}",
543+
redirect_base(req),
544+
name,
545+
version,
546+
target_name
547+
)),
548+
);
549+
550+
let mut resp = Response::with((status::Found, Redirect(url)));
551+
resp.headers.set(Expires(HttpDate(time::now())));
552+
553+
return Ok(resp);
554+
} else {
555+
::log::warn!("retrying random crate search");
556+
}
557+
}
558+
::log::error!("found no result in random crate search");
559+
560+
Err(Nope::NoResults.into())
561+
}
562+
500563
impl_webpage! {
501564
Search = "releases/releases.html",
502565
status = |search| search.status,
@@ -511,53 +574,14 @@ pub fn search_handler(req: &mut Request) -> IronResult<Response> {
511574
if let Some((_, query)) = query {
512575
// check if I am feeling lucky button pressed and redirect user to crate page
513576
// if there is a match
514-
// TODO: Redirecting to latest doc might be more useful
515577
// NOTE: calls `query_pairs()` again because iterators are lazy and only yield items once
516578
if url
517579
.query_pairs()
518580
.any(|(key, _)| key == "i-am-feeling-lucky")
519581
{
520582
// redirect to a random crate if query is empty
521583
if query.is_empty() {
522-
// FIXME: This is a fast query but using a constant
523-
// There are currently 280 crates with docs and 100+
524-
// starts. This should be fine for a while.
525-
let rows = ctry!(
526-
req,
527-
conn.query(
528-
"SELECT crates.name,
529-
releases.version,
530-
releases.target_name
531-
FROM crates
532-
INNER JOIN releases
533-
ON crates.latest_version_id = releases.id
534-
LEFT JOIN github_repos
535-
ON releases.github_repo = github_repos.id
536-
WHERE github_repos.stars >= 100 AND rustdoc_status = true
537-
OFFSET FLOOR(RANDOM() * 280) LIMIT 1",
538-
&[]
539-
),
540-
);
541-
let row = rows.into_iter().next().unwrap();
542-
543-
let name: String = row.get("name");
544-
let version: String = row.get("version");
545-
let target_name: String = row.get("target_name");
546-
let url = ctry!(
547-
req,
548-
Url::parse(&format!(
549-
"{}/{}/{}/{}",
550-
redirect_base(req),
551-
name,
552-
version,
553-
target_name
554-
)),
555-
);
556-
557-
let mut resp = Response::with((status::Found, Redirect(url)));
558-
resp.headers.set(Expires(HttpDate(time::now())));
559-
560-
return Ok(resp);
584+
return redirect_to_random_crate(req, &mut conn);
561585
}
562586

563587
// since we never pass a version into `match_version` here, we'll never get
@@ -1064,6 +1088,18 @@ mod tests {
10641088
})
10651089
}
10661090

1091+
#[test]
1092+
fn im_feeling_lucky_with_stars() {
1093+
wrapper(|env| {
1094+
let web = env.frontend();
1095+
env.fake_release()
1096+
.github_stats("some/repo", 333, 22, 11)
1097+
.name("some_random_crate")
1098+
.create()?;
1099+
assert_success("/releases/search?query=&i-am-feeling-lucky=1", web)
1100+
})
1101+
}
1102+
10671103
#[test]
10681104
fn search() {
10691105
wrapper(|env| {

0 commit comments

Comments
 (0)