diff --git a/src/error.rs b/src/error.rs index 8fd29dc..c010d5d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -59,6 +59,10 @@ pub enum Error { #[error("LMDB: {0}")] Lmdb(#[from] heed::Error), + // Filter is underspecified + #[error("Filter is underspecified. Scrapers are not allowed")] + Scraper, + // UTF-8 #[error("UTF-8: {0}")] Utf8(#[from] std::str::Utf8Error), diff --git a/src/store/mod.rs b/src/store/mod.rs index 2b248f8..88f7eea 100644 --- a/src/store/mod.rs +++ b/src/store/mod.rs @@ -2,10 +2,11 @@ pub mod event_store; pub use event_store::EventStore; use crate::error::Error; -use crate::types::{Event, Id, Kind, Pubkey, Time}; +use crate::types::{Event, Filter, Id, Kind, Pubkey, Time}; use heed::types::{OwnedType, UnalignedSlice}; use heed::{Database, Env, EnvFlags, EnvOpenOptions}; use std::fs; +use std::ops::Bound; #[derive(Debug)] pub struct Store { @@ -153,6 +154,152 @@ impl Store { } } + pub fn find_events(&self, filter: Filter) -> Result, Error> { + let mut output: Vec = Vec::new(); + + if filter.num_ids() > 0 { + // Fetch by id + for id in filter.ids() { + if let Some(event) = self.get_event_by_id(id)? { + // and check each against the rest of the filter + if filter.event_matches(&event)? { + output.push(event); + } + } + // Stop if limited + if output.len() >= filter.limit() as usize { + return Ok(output); + } + } + } else if filter.num_authors() > 0 && filter.num_kinds() > 0 { + for author in filter.authors() { + for kind in filter.kinds() { + let start_prefix = Self::key_akci( + author, + kind, + filter.until(), // scan goes backwards in time + Id([0; 32]), + ); + let end_prefix = Self::key_akci( + author, + kind, + filter.since(), // scan goes backwards in time + Id([255; 32]), + ); + let range = ( + Bound::Included(&*start_prefix), + Bound::Excluded(&*end_prefix), + ); + let txn = self.env.read_txn()?; + let iter = self.akci.range(&txn, &range)?; + for result in iter { + let (_key, offset) = result?; + if let Some(event) = self.events.get_event_by_offset(offset)? { + // check against the rest of the filter + if filter.event_matches(&event)? { + output.push(event); + } + } + // Stop if limited + if output.len() >= filter.limit() as usize { + return Ok(output); + } + } + } + } + } else if filter.num_authors() > 0 && !filter.tags()?.is_empty() { + for author in filter.authors() { + let tags = filter.tags()?; + for mut tag in tags.iter() { + if let Some(tag0) = tag.next() { + if let Some(tagvalue) = tag.next() { + let start_prefix = Self::key_atci( + author, + tag0[0], + tagvalue, + filter.until(), // scan goes backwards in time + Id([0; 32]), + ); + let end_prefix = Self::key_atci( + author, + tag0[0], + tagvalue, + filter.since(), // scan goes backwards in time + Id([255; 32]), + ); + let range = ( + Bound::Included(&*start_prefix), + Bound::Excluded(&*end_prefix), + ); + let txn = self.env.read_txn()?; + let iter = self.akci.range(&txn, &range)?; + for result in iter { + let (_key, offset) = result?; + if let Some(event) = self.events.get_event_by_offset(offset)? { + // check against the rest of the filter + if filter.event_matches(&event)? { + output.push(event); + } + } + // Stop if limited + if output.len() >= filter.limit() as usize { + return Ok(output); + } + } + } + } + } + } + } else if filter.num_kinds() > 0 && !filter.tags()?.is_empty() { + for kind in filter.kinds() { + let tags = filter.tags()?; + for mut tag in tags.iter() { + if let Some(tag0) = tag.next() { + if let Some(tagvalue) = tag.next() { + let start_prefix = Self::key_ktci( + kind, + tag0[0], + tagvalue, + filter.until(), // scan goes backwards in time + Id([0; 32]), + ); + let end_prefix = Self::key_ktci( + kind, + tag0[0], + tagvalue, + filter.since(), // scan goes backwards in time + Id([255; 32]), + ); + let range = ( + Bound::Included(&*start_prefix), + Bound::Excluded(&*end_prefix), + ); + let txn = self.env.read_txn()?; + let iter = self.akci.range(&txn, &range)?; + for result in iter { + let (_key, offset) = result?; + if let Some(event) = self.events.get_event_by_offset(offset)? { + // check against the rest of the filter + if filter.event_matches(&event)? { + output.push(event); + } + } + // Stop if limited + if output.len() >= filter.limit() as usize { + return Ok(output); + } + } + } + } + } + } + } else { + return Err(Error::Scraper); + } + + Ok(output) + } + // For looking up event by Author and Kind // author(32) + kind(2) + reversecreatedat(8) + id(32) #[allow(dead_code)]