Skip to content

Commit 1b2fdd2

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 7b7dd55 commit 1b2fdd2

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};
@@ -182,8 +182,6 @@ fn get_search_results(
182182
struct CratesIoRelease {
183183
name: String,
184184
max_version: String,
185-
description: Option<String>,
186-
updated_at: DateTime<Utc>,
187185
}
188186
#[derive(Deserialize)]
189187
struct CratesIoMeta {
@@ -205,47 +203,57 @@ fn get_search_results(
205203
.unwrap()
206204
});
207205

208-
let page: &str = &page.to_string();
209206
let url = url::Url::parse_with_params(
210207
"https://crates.io/api/v1/crates",
211208
&[
212-
("page", page),
213-
("per_page", &limit.to_string()),
214209
("q", query),
210+
("page", &page.to_string()),
211+
("per_page", &limit.to_string()),
215212
],
216213
)?;
217214
debug!("fetching search results from {}", url);
218215
let releases: CratesIoReleases = HTTP_CLIENT.get(url).send()?.json()?;
219-
let query = conn.prepare(
220-
"SELECT github_repos.stars, releases.target_name, releases.rustdoc_status
216+
let (names_and_versions, names): (Vec<_>, Vec<_>) = releases
217+
.crates
218+
.into_iter()
219+
// The `postgres` crate doesn't support anonymous records.
220+
// Use strings instead.
221+
// Additionally, looking at both the name and version doesn't allow using the index;
222+
// first filter by crate name so the query is more efficient.
223+
.map(|krate| (format!("{}:{}", krate.name, krate.max_version), krate.name))
224+
.unzip();
225+
trace!("crates.io search results {:#?}", names_and_versions);
226+
let crates = conn
227+
.query(
228+
"
229+
SELECT
230+
crates.name,
231+
releases.version,
232+
releases.description,
233+
releases.release_time,
234+
releases.target_name,
235+
releases.rustdoc_status,
236+
github_repos.stars
221237
FROM crates INNER JOIN releases ON crates.id = releases.crate_id
222238
LEFT JOIN github_repos ON releases.github_repo = github_repos.id
223-
WHERE crates.name = $1 AND releases.version = $2",
224-
)?;
225-
let crates = releases
226-
.crates
239+
WHERE crates.name = ANY($1) AND crates.name || ':' || releases.version = ANY($2)
240+
",
241+
&[&names, &names_and_versions],
242+
)?
227243
.into_iter()
228-
.flat_map(|krate| {
229-
let rows = match conn.query(&query, &[&krate.name, &krate.max_version]) {
230-
Err(e) => return Some(Err(e)),
231-
Ok(rows) => rows,
232-
};
233-
debug!("looking up results for {:?}", krate);
234-
// crates.io could have a release that hasn't yet been added to the database.
235-
// If so, just skip it.
236-
let row = rows.get(0)?;
244+
.map(|row| {
237245
let stars: Option<_> = row.get("stars");
238-
Some(Result::<_, postgres::Error>::Ok(Release {
239-
name: krate.name,
240-
version: krate.max_version,
241-
description: krate.description,
242-
release_time: krate.updated_at,
246+
Release {
247+
name: row.get("name"),
248+
version: row.get("version"),
249+
description: row.get("description"),
250+
release_time: row.get("release_time"),
243251
target_name: row.get("target_name"),
244252
rustdoc_status: row.get("rustdoc_status"),
245253
stars: stars.unwrap_or(0),
246-
}))
254+
}
247255
})
248-
.collect::<Result<_, _>>()?;
256+
.collect();
249257
Ok((releases.meta.total, crates))
250258
}
251259

0 commit comments

Comments
 (0)