@@ -251,69 +251,50 @@ fn get_releases_by_owner(
251
251
( author_name, packages)
252
252
}
253
253
254
- /// Get the search results for a search query
254
+ /// Get the search results for a crate search query
255
255
///
256
256
/// Retrieves crates which names have a levenshtein distance of less than or equal to 3,
257
- /// crates who fit into or otherwise are made up of the query or crates who's descriptions
257
+ /// crates who fit into or otherwise are made up of the query or crates whose descriptions
258
258
/// match the search query.
259
259
///
260
260
/// * `query`: The query string, unfiltered
261
261
/// * `page`: The page of results to show (1-indexed)
262
262
/// * `limit`: The number of results to return
263
263
///
264
- /// Returns `None` if no results are found and `Some` with the total number of results and the
265
- /// currently requested results
264
+ /// Returns 0 and an empty Vec when no results are found or if a database error occurs
266
265
///
267
266
fn get_search_results (
268
267
conn : & Connection ,
269
268
query : & str ,
270
269
page : i64 ,
271
270
limit : i64 ,
272
271
) -> ( i64 , Vec < Release > ) {
273
- let query = query. trim ( ) . to_lowercase ( ) ;
272
+ query = query. trim ( ) ;
274
273
let offset = ( page - 1 ) * limit;
275
274
276
- let statement = "SELECT
277
- crates.name,
278
- latest_release.version AS version,
279
- latest_release.description AS description,
280
- latest_release.target_name AS target_name,
281
- latest_release.release_time AS release_time,
282
- latest_release.rustdoc_status AS rustdoc_status,
275
+ let statement =
276
+ "SELECT crates.name,
277
+ -- NOTE: this selects the latest alphanumeric version, which may not be the latest semver
278
+ MAX(releases.version) AS version,
279
+ MAX(releases.description) AS description,
280
+ MAX(releases.target_name) AS target_name,
281
+ MAX(releases.release_time) AS release_time,
282
+ -- Cast the boolean into an integer and then cast it into a boolean.
283
+ -- Posgres moves in mysterious ways, don't question it
284
+ CAST(MAX(releases.rustdoc_status::integer) AS boolean) as rustdoc_status,
283
285
crates.github_stars,
284
- SUM(releases.downloads) AS downloads,
285
- -- Get the total number of results, disregarding the limit
286
- COUNT(*) OVER() as total,
286
+ crates.downloads_total as downloads,
287
287
288
288
-- The levenshtein distance between the search query and the crate's name
289
- levenshtein_less_equal(CAST($1 AS TEXT), CAST( crates.name AS TEXT) , 3) as distance,
289
+ levenshtein_less_equal($1, crates.name, 3) as distance,
290
290
-- The similarity of the tokens of the search vs the tokens of `crates.content`.
291
291
-- The `32` normalizes the number by using `rank / (rank + 1)`
292
- ts_rank_cd(crates.content, plainto_tsquery($1), 32) as content_rank
293
- FROM
294
- crates
295
- INNER JOIN releases ON releases.crate_id = crates.id
296
- INNER JOIN (
297
- SELECT DISTINCT ON (crate_id)
298
- crate_id,
299
- version,
300
- description,
301
- target_name,
302
- release_time,
303
- rustdoc_status,
304
- yanked
305
- FROM
306
- releases
307
- ORDER BY
308
- crate_id,
309
- release_time DESC
310
- ) AS latest_release ON latest_release.crate_id = crates.id
311
-
312
- -- Filter crates that haven't been built and crates that have been yanked and
313
- -- crates that don't match the query closely enough
314
- WHERE
315
- latest_release.rustdoc_status
316
- AND NOT latest_release.yanked
292
+ ts_rank_cd(crates.content, to_tsquery($2), 32) as content_rank
293
+ FROM releases INNER JOIN crates on releases.crate_id = crates.id
294
+
295
+ -- Filter crates that haven't been built and crates that have been yanked
296
+ WHERE releases.rustdoc_status = true
297
+ AND releases.yanked = false
317
298
AND (
318
299
-- Crates names that match the query sandwiched between wildcards will pass
319
300
crates.name ILIKE CONCAT('%', $1, '%')
@@ -326,25 +307,22 @@ fn get_search_results(
326
307
GROUP BY crates.id, releases.id
327
308
328
309
-- Ordering is prioritized by how closely the query matches the name, how closely the
329
- -- query matches the description finally how many downloads the crate has
330
- ORDER BY
331
- distance ASC ,
310
+ -- query matches the description, and finally how many downloads the crate has
311
+ -- NOTE: this means that exact matches will be shown first
312
+ ORDER BY distance DESC ,
332
313
content_rank DESC,
333
314
downloads_total DESC
334
315
335
316
-- Allows pagination
336
317
LIMIT $2 OFFSET $3" ;
337
318
338
- let rows = if let Ok ( rows) = conn
339
- . query ( statement, & [ & query, & limit, & offset] )
340
- . map_err ( |err| dbg ! ( err) )
341
- {
319
+ let rows = if let Ok ( rows) = conn. query ( statement, & [ & query, & limit, & offset] ) {
342
320
rows
343
321
} else {
344
322
return ( 0 , Vec :: new ( ) ) ;
345
323
} ;
346
324
347
- let total_results: i64 = rows. iter ( ) . next ( ) . map ( |row| row. get ( 8 ) ) . unwrap_or_default ( ) ;
325
+ let total_results = rows. iter ( ) . map ( |row| row. get :: < _ , i64 > ( 8 ) ) . sum ( ) ;
348
326
let packages: Vec < Release > = rows
349
327
. into_iter ( )
350
328
. map ( |row| Release {
@@ -736,7 +714,7 @@ mod tests {
736
714
for expected in expected. iter ( ) {
737
715
assert_eq ! ( expected, & results. next( ) . unwrap( ) . name) ;
738
716
}
739
- assert ! ( results. collect :: < Vec <_>> ( ) . is_empty ( ) ) ;
717
+ assert_eq ! ( results. count ( ) , 0 ) ;
740
718
741
719
Ok ( ( ) )
742
720
} )
@@ -763,7 +741,7 @@ mod tests {
763
741
764
742
#[ test]
765
743
fn exacts_dont_care ( ) {
766
- let near_matches = [ "Regex" , "rEgex" , "reGex" , "regEx" , "regeX" ] ;
744
+ let near_matches = [ "regex" , " Regex", "rEgex" , "reGex" , "regEx" , "regeX" ] ;
767
745
768
746
for name in near_matches. iter ( ) {
769
747
wrapper ( |env| {
@@ -772,7 +750,7 @@ mod tests {
772
750
773
751
non_exact ( & db) ?;
774
752
775
- let ( num_results, results) = get_search_results ( & db. conn ( ) , "foo " , 1 , 100 ) ;
753
+ let ( num_results, results) = get_search_results ( & db. conn ( ) , "regex " , 1 , 100 ) ;
776
754
let mut results = results. into_iter ( ) ;
777
755
778
756
assert_eq ! ( num_results, 4 ) ;
0 commit comments