2
2
3
3
use crate :: {
4
4
build_queue:: QueuedCrate ,
5
- db:: Pool ,
5
+ db:: { Pool , PoolClient } ,
6
6
impl_webpage,
7
7
web:: { error:: Nope , match_version, page:: WebPage , redirect_base} ,
8
8
BuildQueue ,
@@ -497,6 +497,69 @@ impl Default for Search {
497
497
}
498
498
}
499
499
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
+
500
563
impl_webpage ! {
501
564
Search = "releases/releases.html" ,
502
565
status = |search| search. status,
@@ -511,53 +574,14 @@ pub fn search_handler(req: &mut Request) -> IronResult<Response> {
511
574
if let Some ( ( _, query) ) = query {
512
575
// check if I am feeling lucky button pressed and redirect user to crate page
513
576
// if there is a match
514
- // TODO: Redirecting to latest doc might be more useful
515
577
// NOTE: calls `query_pairs()` again because iterators are lazy and only yield items once
516
578
if url
517
579
. query_pairs ( )
518
580
. any ( |( key, _) | key == "i-am-feeling-lucky" )
519
581
{
520
582
// redirect to a random crate if query is empty
521
583
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) ;
561
585
}
562
586
563
587
// since we never pass a version into `match_version` here, we'll never get
@@ -1064,6 +1088,18 @@ mod tests {
1064
1088
} )
1065
1089
}
1066
1090
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
+
1067
1103
#[ test]
1068
1104
fn search ( ) {
1069
1105
wrapper ( |env| {
0 commit comments