feat(NIP-01): Implement limit

This was quickly sneaked in by fiatjaf per my request[0], it makes many
queries more efficient and allows for paging when combined with until.

It is a bit weird to have multiple limits on each filter... for now we
just choose any or the last limit seen.

[0]: a4aea5337f

Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin 2022-05-09 13:39:49 -07:00 committed by Greg Heartsfield
parent 9b351aab9b
commit 4ad483090e
2 changed files with 16 additions and 1 deletions

View File

@ -385,6 +385,7 @@ fn query_from_sub(sub: &Subscription) -> (String, Vec<Box<dyn ToSql>>) {
// (sqli-safe), or a string that is filtered to only contain // (sqli-safe), or a string that is filtered to only contain
// hexadecimal characters. Strings that require escaping (tag // hexadecimal characters. Strings that require escaping (tag
// names/values) use parameters. // names/values) use parameters.
let mut limit: Option<u32> = None;
let mut query = let mut query =
"SELECT DISTINCT(e.content) FROM event e LEFT JOIN tag t ON e.id=t.event_id ".to_owned(); "SELECT DISTINCT(e.content) FROM event e LEFT JOIN tag t ON e.id=t.event_id ".to_owned();
// parameters // parameters
@ -422,6 +423,9 @@ fn query_from_sub(sub: &Subscription) -> (String, Vec<Box<dyn ToSql>>) {
let authors_clause = format!("({})", auth_searches.join(" OR ")); let authors_clause = format!("({})", auth_searches.join(" OR "));
filter_components.push(authors_clause); filter_components.push(authors_clause);
} }
if let Some(lim) = f.limit {
limit = Some(lim)
}
// Query for Kind // Query for Kind
if let Some(ks) = &f.kinds { if let Some(ks) = &f.kinds {
// kind is number, no escaping needed // kind is number, no escaping needed
@ -513,7 +517,13 @@ fn query_from_sub(sub: &Subscription) -> (String, Vec<Box<dyn ToSql>>) {
query.push_str(") "); query.push_str(") ");
} }
// add order clause // add order clause
query.push_str(" ORDER BY created_at ASC"); query.push_str(&format!(
" ORDER BY created_at {}",
limit.map_or("ASC", |_| "DESC")
));
if let Some(lim) = limit {
query.push_str(&format!(" LIMIT {}", lim))
}
debug!("query string: {}", query); debug!("query string: {}", query);
(query, params) (query, params)
} }

View File

@ -31,6 +31,8 @@ pub struct ReqFilter {
pub until: Option<u64>, pub until: Option<u64>,
/// List of author public keys /// List of author public keys
pub authors: Option<Vec<String>>, pub authors: Option<Vec<String>>,
/// Limit number of results
pub limit: Option<u32>,
/// Set of tags /// Set of tags
#[serde(skip)] #[serde(skip)]
pub tags: Option<HashMap<String, HashSet<String>>>, pub tags: Option<HashMap<String, HashSet<String>>>,
@ -54,6 +56,7 @@ impl<'de> Deserialize<'de> for ReqFilter {
since: None, since: None,
until: None, until: None,
authors: None, authors: None,
limit: None,
tags: None, tags: None,
}; };
let mut ts = None; let mut ts = None;
@ -68,6 +71,8 @@ impl<'de> Deserialize<'de> for ReqFilter {
rf.since = Deserialize::deserialize(val).ok(); rf.since = Deserialize::deserialize(val).ok();
} else if key == "until" { } else if key == "until" {
rf.until = Deserialize::deserialize(val).ok(); rf.until = Deserialize::deserialize(val).ok();
} else if key == "limit" {
rf.limit = Deserialize::deserialize(val).ok();
} else if key == "authors" { } else if key == "authors" {
rf.authors = Deserialize::deserialize(val).ok(); rf.authors = Deserialize::deserialize(val).ok();
} else if key.starts_with('#') && key.len() > 1 && val.is_array() { } else if key.starts_with('#') && key.len() > 1 && val.is_array() {