From 30a4fc7365ffa4842275bc9513e3e458308b596d Mon Sep 17 00:00:00 2001 From: "Jakob L. Kreuze" Date: Mon, 25 May 2020 18:06:08 -0400 Subject: Implement a paginated `query` method. --- src/database.rs | 90 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 29 deletions(-) (limited to 'src/database.rs') diff --git a/src/database.rs b/src/database.rs index 77397a5..d27646b 100644 --- a/src/database.rs +++ b/src/database.rs @@ -23,6 +23,14 @@ use std::path::Path; #[derive(Debug)] pub struct TagDatabase(Connection); +/// Tag query with a sense of "state" for pagination. +#[derive(Debug)] +pub struct Query<'a> { + tags: &'a [&'a str], + last_id: Option, + limit: Option, +} + impl TagDatabase { /// Instantiate a new `TagDatabase` whose backing database is at `path`. pub fn new(path: &str) -> Result { @@ -137,49 +145,64 @@ impl TagDatabase { } /// Return the intersection of the sets of images matching each of `tags`. - pub fn images_by_tags(&self, tags: &[&str]) -> Result> { - let ids: Vec = tags + pub fn query(&self, q: Query) -> Result> { + let ids: Vec = q + .tags .iter() .filter_map(|tag| self.tag_id(tag).ok()) .collect(); - let mut tags = Vec::from(tags); + let mut tags = Vec::from(q.tags); tags.dedup(); + // Query contained a tag that's not in the database. if ids.len() != tags.len() { return Ok(vec![]); } let union = if tags.is_empty() { - String::from( - "INNER JOIN mapping - ON images.id = mapping.image", - ) + None } else { - (1..ids.len() + 1) - .zip(ids) - .map(|pair| { - let (n, id) = pair; - format!( - "INNER JOIN mapping as m{n} + Some( + (1..ids.len() + 1) + .zip(ids) + .map(|pair| { + let (n, id) = pair; + format!( + "INNER JOIN mapping as m{n} ON images.id = m{n}.image AND {id} = m{n}.tag", - n = n, - id = id - ) - }) - .collect::>() - .join("\n") + n = n, + id = id + ) + }) + .collect::>() + .join("\n"), + ) + }; + + let query = if let (Some(id), Some(n)) = (q.last_id, q.limit) { + format!( + "SELECT id, blake2, filename, orig_dir + FROM images + {union} + WHERE id > {last_id} + GROUP BY id + ORDER BY id DESC + LIMIT {limit}", + last_id = id, + union = union.or(Some("".into())).unwrap(), + limit = n + ) + } else { + format!( + "SELECT id, blake2, filename, orig_dir + FROM images + {union} + GROUP BY id + ORDER BY id DESC", + union = union.or(Some("".into())).unwrap(), + ) }; - // WHERE id > {lastId} - // LIMIT 50 - let query = format!( - "SELECT id, blake2, filename, orig_dir - FROM images - {union} - GROUP BY id - ORDER BY id DESC", - union = union - ); Ok(self .0 @@ -204,6 +227,15 @@ impl TagDatabase { }) .collect()) } + + /// Return the intersection of the sets of images matching each of `tags`. + pub fn images_by_tags(&self, tags: &[&str]) -> Result> { + self.query(Query { + tags, + last_id: None, + limit: None, + }) + } } /// The result of an image query in бирка-тян's tag database. -- cgit v1.3