From a051760fd97df91cd56827533ee890282699d14f Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 30 Aug 2024 10:39:43 -0700 Subject: [PATCH] contacts: fix hashtags in filter_from_tags split hashtag follows into a separate Filter, combining authors and hashtags doesn't work, because this is considered an AND filter, where we want an OR. We may want an option to split hashtags follows into a separate column. Signed-off-by: William Casarin --- src/filter.rs | 79 ++++++++++++++++++++++++++++++++++++------------- src/timeline.rs | 2 +- 2 files changed, 60 insertions(+), 21 deletions(-) diff --git a/src/filter.rs b/src/filter.rs index 7f48c49..a2f49d7 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -1,6 +1,7 @@ use crate::note::NoteRef; use crate::{Error, Result}; use nostrdb::{Filter, FilterBuilder, Note}; +use tracing::{debug, warn}; pub fn should_since_optimize(limit: u64, num_notes: usize) -> bool { // rough heuristic for bailing since optimization if we don't have enough notes @@ -32,13 +33,45 @@ pub fn default_remote_limit() -> u64 { 150 } +pub struct FilteredTags { + pub authors: Option, + pub hashtags: Option, +} + +impl FilteredTags { + // TODO: make this more general + pub fn into_filter(self, kinds: I) -> Vec + where + I: IntoIterator + Copy, + { + let mut filters: Vec = Vec::with_capacity(2); + + if let Some(authors) = self.authors { + filters.push(authors.kinds(kinds).build()) + } + + if let Some(hashtags) = self.hashtags { + filters.push(hashtags.kinds(kinds).build()) + } + + filters + } +} + /// Create a filter from tags. This can be used to create a filter /// from a contact list -pub fn filter_from_tags(note: &Note) -> Result { - let mut filter = Filter::new(); +pub fn filter_from_tags(note: &Note) -> Result { + let mut author_filter = Filter::new(); + let mut hashtag_filter = Filter::new(); + let mut author_res: Option = None; + let mut hashtag_res: Option = None; + let mut author_count = 0i32; + let mut hashtag_count = 0i32; + let tags = note.tags(); - let mut authors: Vec<&[u8; 32]> = Vec::with_capacity(tags.count() as usize); - let mut hashtags: Vec<&str> = vec![]; + + author_filter.start_authors_field()?; + hashtag_filter.start_tags_field('t')?; for tag in tags { if tag.count() < 2 { @@ -58,7 +91,8 @@ pub fn filter_from_tags(note: &Note) -> Result { continue; }; - authors.push(author); + author_filter.add_id_element(author)?; + author_count += 1; } else if t == "t" { let hashtag = if let Some(hashtag) = tag.get_unchecked(1).variant().str() { hashtag @@ -66,30 +100,35 @@ pub fn filter_from_tags(note: &Note) -> Result { continue; }; - hashtags.push(hashtag); + hashtag_filter.add_str_element(hashtag)?; + hashtag_count += 1; } } - if authors.is_empty() && hashtags.is_empty() { + author_filter.end_field(); + hashtag_filter.end_field(); + + if author_count == 0 && hashtag_count == 0 { + warn!("no authors or hashtags found in contact list"); return Err(Error::EmptyContactList); } + debug!( + "adding {} authors and {} hashtags to contact filter", + author_count, hashtag_count + ); + // if we hit these ooms, we need to expand filter buffer size - if !authors.is_empty() { - filter.start_authors_field()?; - for author in authors { - filter.add_id_element(author)?; - } - filter.end_field(); + if author_count > 0 { + author_res = Some(author_filter) } - if !hashtags.is_empty() { - filter.start_tags_field('t')?; - for hashtag in hashtags { - filter.add_str_element(hashtag)?; - } - filter.end_field(); + if hashtag_count > 0 { + hashtag_res = Some(hashtag_filter) } - Ok(filter) + Ok(FilteredTags { + authors: author_res, + hashtags: hashtag_res, + }) } diff --git a/src/timeline.rs b/src/timeline.rs index 3930635..f55615f 100644 --- a/src/timeline.rs +++ b/src/timeline.rs @@ -337,7 +337,7 @@ pub struct Timeline { impl Timeline { /// Create a timeline from a contact list pub fn contact_list(contact_list: &Note) -> Result { - let filter = vec![filter::filter_from_tags(contact_list)?.kinds([1]).build()]; + let filter = filter::filter_from_tags(contact_list)?.into_filter([1]); let pk_src = PubkeySource::Explicit(Pubkey::new(contact_list.pubkey())); Ok(Timeline::new(