diff --git a/src/data/search.rs b/src/data/search.rs index 7c36083..5f7cd80 100644 --- a/src/data/search.rs +++ b/src/data/search.rs @@ -9,21 +9,30 @@ use std::collections::{ use smallvec::SmallVec; #[derive(Debug)] -pub struct StoreSearchIter<'a, T: ?Sized>(&'a Store, Option>, VecDeque<&'a sha256::Sha256Hash>, PhantomData); +pub struct StoreSearchAnyIter<'a, T: ?Sized>(&'a Store, Option>, VecDeque<&'a sha256::Sha256Hash>, PhantomData); + +#[derive(Debug)] +pub struct StoreSearchAllIter<'a, T: ?Sized>(&'a Store, Option>, VecDeque<&'a Entry>, SmallVec<[&'a T; 8]>); // Accessing impl Store { + /// Search for all entries with *all* of these provided tags. + /// + /// # Notes + /// This is allowed to produce duplicate entries, if either: + /// * An entry has multiple of the same tag set + /// * An entry has multiple of the tags provided to this function set //TODO: Parallelize this with futures::Stream ver to do the `tag_search` lookups in parallel. - pub fn tag_search_all<'a, T: ?Sized + Ord + 'a>(&'a self, tags: impl IntoIterator) -> impl Iterator + 'a + pub fn tag_search_all<'a, T: ?Sized + Ord + 'a>(&'a self, tags: impl IntoIterator) -> StoreSearchAllIter<'_, T> where String: Borrow - { - let tags: SmallVec<[_; 5]> = tags.into_iter().collect(); - - tags.iter().flat_map(|&tag| self.tag_search(tag)).dedup_ref() - .filter(|x| x.tags.iter().map(|x| x.borrow()).filter(|tx| !tags.contains(tx)).count() == 0) - - /* TODO: get rid of this ,replace `impl Iterator` return with its own type. */ .collect::>().into_iter() + { + let mut sorted: SmallVec<[_; 8]> = tags.into_iter().collect(); + sorted.sort(); + StoreSearchAllIter(self, Some(match (sorted.first(), sorted.last()) { + (Some(&low), Some(&high)) => self.tags.range::((std::ops::Bound::Included(low), std::ops::Bound::Included(high))), + _ => return StoreSearchAllIter(self, None, Default::default(), sorted), + }), VecDeque::new(), sorted) } /// Search for all entries with *any* of these provided tags. /// @@ -31,34 +40,74 @@ impl Store /// This is allowed to produce duplicate entries, if either: /// * An entry has multiple of the same tag set /// * An entry has multiple of the tags provided to this function set - pub fn tag_search_any<'a, T: ?Sized + Ord + 'a>(&self, tags: impl IntoIterator) -> StoreSearchIter<'_, T> + pub fn tag_search_any<'a, T: ?Sized + Ord + 'a>(&self, tags: impl IntoIterator) -> StoreSearchAnyIter<'_, T> where String: Borrow { - let mut sorted: SmallVec<[_; 5]> = tags.into_iter().collect(); + let mut sorted: SmallVec<[_; 8]> = tags.into_iter().collect(); sorted.sort(); - StoreSearchIter(self, Some(match (sorted.first(), sorted.last()) { + StoreSearchAnyIter(self, Some(match (sorted.first(), sorted.last()) { (Some(&low), Some(&high)) => self.tags.range::((std::ops::Bound::Included(low), std::ops::Bound::Included(high))), - _ => return StoreSearchIter(self, None, Default::default(), PhantomData), + _ => return StoreSearchAnyIter(self, None, Default::default(), PhantomData), }), VecDeque::new(), PhantomData) } /// Search for all items with this provided tag. /// /// # Notes /// This is allowed to produce duplicate entries, if an entry has two of the same tags set. - pub fn tag_search<'a, T: ?Sized + Ord>(&'a self, tag: &T) -> StoreSearchIter<'a, T> + pub fn tag_search<'a, T: ?Sized + Ord>(&'a self, tag: &T) -> StoreSearchAnyIter<'a, T> where String: Borrow { let r= (std::ops::Bound::Included(tag), std::ops::Bound::Included(tag)); - StoreSearchIter(self, Some(self.tags.range::(r)), VecDeque::new(), PhantomData) + StoreSearchAnyIter(self, Some(self.tags.range::(r)), VecDeque::new(), PhantomData) } fn _assert_test_search(&self) { + let _x: Vec<_> = self.tag_search("hello").dedup_ref().collect(); let _x: Vec<_> = self.tag_search_any(vec!["hello", "one", "two"]).dedup_ref().collect(); + let _x: Vec<_> = self.tag_search_all(vec!["hello", "one", "two"]).dedup_ref().collect(); } } -impl<'a, T: ?Sized> Iterator for StoreSearchIter<'a, T> +impl<'a, T: ?Sized> Iterator for StoreSearchAllIter<'a, T> +where T: Ord, + String: Borrow +{ + type Item = &'a Entry; + fn next(&mut self) -> Option + { + if let Some(range) = &mut self.1 { + if let Some((_, &ti)) = range.next() { + // tag index get + let data_hashes = &self.0.data_hashes; + let data = &self.0.data; + let tags = &self.3; + + let iter = self.0.tag_mappings.get(ti) + .map_into_iter() + .filter_map(move |&idx| data_hashes.get(idx)) + .filter_map(move |x| data.get(x)) + // Ensure all our `tags` are present in the entry's tags + .filter(move |entry| tags.iter() + .filter(move |&x| entry.tags + .binary_search_by_key(x, |t: &String| -> &T { t.borrow() }) + .is_err()) + .count() == 0); + self.2.extend(iter); + } + } else { + return None; + } + self.2.pop_front() + } + fn size_hint(&self) -> (usize, Option) { + match self.1 { + None => (0, Some(0)), + Some(_) => (0, None), + } + } +} +impl<'a, T: ?Sized> Iterator for StoreSearchAnyIter<'a, T> { type Item = &'a Entry; fn next(&mut self) -> Option @@ -84,4 +133,4 @@ impl<'a, T: ?Sized> Iterator for StoreSearchIter<'a, T> } } } -impl<'a, T: ?Sized> iter::FusedIterator for StoreSearchIter<'a, T>{} +impl<'a, T: ?Sized> iter::FusedIterator for StoreSearchAnyIter<'a, T>{} diff --git a/src/ext.rs b/src/ext.rs index 24aa14e..f19da8f 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -137,23 +137,20 @@ where I: Iterator + DoubleEndedIterator impl<'a, I, T:?Sized + 'a> FusedIterator for DedupedRefIter<'a, I, T> where I: Iterator + FusedIterator{} -pub trait ContainsAllExt -where U: AsRef, - I: IntoIterator, +pub trait ContainsAllExt { - fn contains_all(&self, items: I) -> bool; + fn contains_all>(&self, items: V) -> bool; } -impl ContainsAllExt for S +impl ContainsAllExt for S where S: AsRef<[T]>, - U: AsRef, - I: IntoIterator, T: PartialEq, { - fn contains_all(&self, items: I) -> bool { - for x in items.into_iter() + fn contains_all>(&self, items: V) -> bool { + let s = self.as_ref(); + for x in items.as_ref().iter() { - if !self.as_ref().contains(x.as_ref()) { + if !s.contains(x) { return false; } }