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 <jb55@jb55.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
use crate::note::NoteRef;
|
use crate::note::NoteRef;
|
||||||
use crate::{Error, Result};
|
use crate::{Error, Result};
|
||||||
use nostrdb::{Filter, FilterBuilder, Note};
|
use nostrdb::{Filter, FilterBuilder, Note};
|
||||||
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
pub fn should_since_optimize(limit: u64, num_notes: usize) -> bool {
|
pub fn should_since_optimize(limit: u64, num_notes: usize) -> bool {
|
||||||
// rough heuristic for bailing since optimization if we don't have enough notes
|
// rough heuristic for bailing since optimization if we don't have enough notes
|
||||||
@@ -32,13 +33,45 @@ pub fn default_remote_limit() -> u64 {
|
|||||||
150
|
150
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FilteredTags {
|
||||||
|
pub authors: Option<FilterBuilder>,
|
||||||
|
pub hashtags: Option<FilterBuilder>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilteredTags {
|
||||||
|
// TODO: make this more general
|
||||||
|
pub fn into_filter<I>(self, kinds: I) -> Vec<Filter>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = u64> + Copy,
|
||||||
|
{
|
||||||
|
let mut filters: Vec<Filter> = 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
|
/// Create a filter from tags. This can be used to create a filter
|
||||||
/// from a contact list
|
/// from a contact list
|
||||||
pub fn filter_from_tags(note: &Note) -> Result<FilterBuilder> {
|
pub fn filter_from_tags(note: &Note) -> Result<FilteredTags> {
|
||||||
let mut filter = Filter::new();
|
let mut author_filter = Filter::new();
|
||||||
|
let mut hashtag_filter = Filter::new();
|
||||||
|
let mut author_res: Option<FilterBuilder> = None;
|
||||||
|
let mut hashtag_res: Option<FilterBuilder> = None;
|
||||||
|
let mut author_count = 0i32;
|
||||||
|
let mut hashtag_count = 0i32;
|
||||||
|
|
||||||
let tags = note.tags();
|
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 {
|
for tag in tags {
|
||||||
if tag.count() < 2 {
|
if tag.count() < 2 {
|
||||||
@@ -58,7 +91,8 @@ pub fn filter_from_tags(note: &Note) -> Result<FilterBuilder> {
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
authors.push(author);
|
author_filter.add_id_element(author)?;
|
||||||
|
author_count += 1;
|
||||||
} else if t == "t" {
|
} else if t == "t" {
|
||||||
let hashtag = if let Some(hashtag) = tag.get_unchecked(1).variant().str() {
|
let hashtag = if let Some(hashtag) = tag.get_unchecked(1).variant().str() {
|
||||||
hashtag
|
hashtag
|
||||||
@@ -66,30 +100,35 @@ pub fn filter_from_tags(note: &Note) -> Result<FilterBuilder> {
|
|||||||
continue;
|
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);
|
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 we hit these ooms, we need to expand filter buffer size
|
||||||
if !authors.is_empty() {
|
if author_count > 0 {
|
||||||
filter.start_authors_field()?;
|
author_res = Some(author_filter)
|
||||||
for author in authors {
|
|
||||||
filter.add_id_element(author)?;
|
|
||||||
}
|
|
||||||
filter.end_field();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hashtags.is_empty() {
|
if hashtag_count > 0 {
|
||||||
filter.start_tags_field('t')?;
|
hashtag_res = Some(hashtag_filter)
|
||||||
for hashtag in hashtags {
|
|
||||||
filter.add_str_element(hashtag)?;
|
|
||||||
}
|
|
||||||
filter.end_field();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(filter)
|
Ok(FilteredTags {
|
||||||
|
authors: author_res,
|
||||||
|
hashtags: hashtag_res,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -337,7 +337,7 @@ pub struct Timeline {
|
|||||||
impl Timeline {
|
impl Timeline {
|
||||||
/// Create a timeline from a contact list
|
/// Create a timeline from a contact list
|
||||||
pub fn contact_list(contact_list: &Note) -> Result<Self> {
|
pub fn contact_list(contact_list: &Note) -> Result<Self> {
|
||||||
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()));
|
let pk_src = PubkeySource::Explicit(Pubkey::new(contact_list.pubkey()));
|
||||||
|
|
||||||
Ok(Timeline::new(
|
Ok(Timeline::new(
|
||||||
|
|||||||
Reference in New Issue
Block a user