Skip to content

Commit f029d0b

Browse files
committed
Simplify and speed up database queries
- Only make one queries, instead of one per each crate - Remove confusing filter_map and Option<Result> - Filter by crate name to reuse the database index
1 parent fb5e5a3 commit f029d0b

File tree

1 file changed

+36
-28
lines changed

1 file changed

+36
-28
lines changed

src/web/releases.rs

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use iron::{
1414
modifiers::Redirect,
1515
status, IronResult, Request, Response, Url,
1616
};
17-
use log::debug;
17+
use log::{debug, trace};
1818
use postgres::Client;
1919
use router::Router;
2020
use serde::{Deserialize, Serialize};
@@ -232,8 +232,6 @@ fn get_search_results(
232232
struct CratesIoRelease {
233233
name: String,
234234
max_version: String,
235-
description: Option<String>,
236-
updated_at: DateTime<Utc>,
237235
}
238236
#[derive(Deserialize)]
239237
struct CratesIoMeta {
@@ -255,47 +253,57 @@ fn get_search_results(
255253
.unwrap()
256254
});
257255

258-
let page: &str = &page.to_string();
259256
let url = url::Url::parse_with_params(
260257
"https://crates.io/api/v1/crates",
261258
&[
262-
("page", page),
263-
("per_page", &limit.to_string()),
264259
("q", query),
260+
("page", &page.to_string()),
261+
("per_page", &limit.to_string()),
265262
],
266263
)?;
267264
debug!("fetching search results from {}", url);
268265
let releases: CratesIoReleases = HTTP_CLIENT.get(url).send()?.json()?;
269-
let query = conn.prepare(
270-
"SELECT github_repos.stars, releases.target_name, releases.rustdoc_status
266+
let (names_and_versions, names): (Vec<_>, Vec<_>) = releases
267+
.crates
268+
.into_iter()
269+
// The `postgres` crate doesn't support anonymous records.
270+
// Use strings instead.
271+
// Additionally, looking at both the name and version doesn't allow using the index;
272+
// first filter by crate name so the query is more efficient.
273+
.map(|krate| (format!("{}:{}", krate.name, krate.max_version), krate.name))
274+
.unzip();
275+
trace!("crates.io search results {:#?}", names_and_versions);
276+
let crates = conn
277+
.query(
278+
"
279+
SELECT
280+
crates.name,
281+
releases.version,
282+
releases.description,
283+
releases.release_time,
284+
releases.target_name,
285+
releases.rustdoc_status,
286+
github_repos.stars
271287
FROM crates INNER JOIN releases ON crates.id = releases.crate_id
272288
LEFT JOIN github_repos ON releases.github_repo = github_repos.id
273-
WHERE crates.name = $1 AND releases.version = $2",
274-
)?;
275-
let crates = releases
276-
.crates
289+
WHERE crates.name = ANY($1) AND crates.name || ':' || releases.version = ANY($2)
290+
",
291+
&[&names, &names_and_versions],
292+
)?
277293
.into_iter()
278-
.flat_map(|krate| {
279-
let rows = match conn.query(&query, &[&krate.name, &krate.max_version]) {
280-
Err(e) => return Some(Err(e)),
281-
Ok(rows) => rows,
282-
};
283-
debug!("looking up results for {:?}", krate);
284-
// crates.io could have a release that hasn't yet been added to the database.
285-
// If so, just skip it.
286-
let row = rows.get(0)?;
294+
.map(|row| {
287295
let stars: Option<_> = row.get("stars");
288-
Some(Result::<_, postgres::Error>::Ok(Release {
289-
name: krate.name,
290-
version: krate.max_version,
291-
description: krate.description,
292-
release_time: krate.updated_at,
296+
Release {
297+
name: row.get("name"),
298+
version: row.get("version"),
299+
description: row.get("description"),
300+
release_time: row.get("release_time"),
293301
target_name: row.get("target_name"),
294302
rustdoc_status: row.get("rustdoc_status"),
295303
stars: stars.unwrap_or(0),
296-
}))
304+
}
297305
})
298-
.collect::<Result<_, _>>()?;
306+
.collect();
299307
Ok((releases.meta.total, crates))
300308
}
301309

0 commit comments

Comments
 (0)