mirror of
https://github.com/qdrant/qdrant.git
synced 2026-09-21 13:37:46 -05:00
Propagate OperationResult in PayloadFieldIndex::filter (#8647)
This commit is contained in:
@@ -150,7 +150,7 @@ impl PayloadFieldIndex for BoolIndex {
|
||||
&'a self,
|
||||
condition: &'a crate::types::FieldCondition,
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
) -> OperationResult<Option<Box<dyn Iterator<Item = PointOffsetType> + 'a>>> {
|
||||
) -> Option<Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a>> {
|
||||
match self {
|
||||
BoolIndex::Mmap(index) => index.filter(condition, hw_counter),
|
||||
}
|
||||
@@ -311,8 +311,8 @@ mod tests {
|
||||
let count = index
|
||||
.filter(&match_bool(match_on), &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.count();
|
||||
.process_results(|it| it.count())
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(count, expected_count);
|
||||
}
|
||||
@@ -372,14 +372,14 @@ mod tests {
|
||||
let point_offsets = new_index
|
||||
.filter(&match_bool(false), &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect_vec();
|
||||
assert_eq!(point_offsets, vec![1, 2, 3, 5, 6, 10]);
|
||||
|
||||
let point_offsets = new_index
|
||||
.filter(&match_bool(true), &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect_vec();
|
||||
assert_eq!(point_offsets, vec![0, 2, 3, 4, 6, 11]);
|
||||
|
||||
@@ -409,7 +409,7 @@ mod tests {
|
||||
let point_offsets = index
|
||||
.filter(&match_bool(false), &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect_vec();
|
||||
assert_eq!(point_offsets, vec![idx]);
|
||||
|
||||
@@ -418,13 +418,13 @@ mod tests {
|
||||
let point_offsets = index
|
||||
.filter(&match_bool(true), &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect_vec();
|
||||
assert_eq!(point_offsets, vec![idx]);
|
||||
let point_offsets = index
|
||||
.filter(&match_bool(false), &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect_vec();
|
||||
assert!(point_offsets.is_empty());
|
||||
}
|
||||
|
||||
@@ -376,8 +376,8 @@ impl PayloadFieldIndex for MutableBoolIndex {
|
||||
&'a self,
|
||||
condition: &'a FieldCondition,
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
) -> OperationResult<Option<Box<dyn Iterator<Item = PointOffsetType> + 'a>>> {
|
||||
Ok(match &condition.r#match {
|
||||
) -> Option<Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a>> {
|
||||
match &condition.r#match {
|
||||
Some(Match::Value(MatchValue {
|
||||
value: ValueVariants::Bool(value),
|
||||
})) => {
|
||||
@@ -389,11 +389,12 @@ impl PayloadFieldIndex for MutableBoolIndex {
|
||||
hw_counter.new_accumulator(),
|
||||
u8::BITS as usize,
|
||||
|i| i.payload_index_io_read_counter(),
|
||||
);
|
||||
)
|
||||
.map(Ok);
|
||||
Some(Box::new(iter))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn estimate_cardinality(
|
||||
|
||||
@@ -52,7 +52,7 @@ pub trait PayloadFieldIndex {
|
||||
&'a self,
|
||||
condition: &'a FieldCondition,
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
) -> OperationResult<Option<Box<dyn Iterator<Item = PointOffsetType> + 'a>>>;
|
||||
) -> Option<Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a>>;
|
||||
|
||||
/// Return estimation of amount of points which satisfy given condition.
|
||||
/// Returns `Ok(None)` if the condition does not match the index type
|
||||
@@ -245,7 +245,7 @@ impl FieldIndex {
|
||||
&'a self,
|
||||
condition: &'a FieldCondition,
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
) -> OperationResult<Option<Box<dyn Iterator<Item = PointOffsetType> + 'a>>> {
|
||||
) -> Option<Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a>> {
|
||||
self.get_payload_field_index().filter(condition, hw_counter)
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -262,11 +262,11 @@ impl InvertedIndex for ImmutableInvertedIndex {
|
||||
&'a self,
|
||||
query: ParsedQuery,
|
||||
_hw_counter: &'a HardwareCounterCell,
|
||||
) -> Box<dyn Iterator<Item = PointOffsetType> + 'a> {
|
||||
) -> Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a> {
|
||||
match query {
|
||||
ParsedQuery::AllTokens(tokens) => Box::new(self.filter_has_all(tokens)),
|
||||
ParsedQuery::Phrase(tokens) => Box::new(self.filter_has_phrase(tokens)),
|
||||
ParsedQuery::AnyTokens(tokens) => Box::new(self.filter_has_any(tokens)),
|
||||
ParsedQuery::AllTokens(tokens) => Box::new(self.filter_has_all(tokens).map(Ok)),
|
||||
ParsedQuery::Phrase(tokens) => Box::new(self.filter_has_phrase(tokens).map(Ok)),
|
||||
ParsedQuery::AnyTokens(tokens) => Box::new(self.filter_has_any(tokens).map(Ok)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-9
@@ -190,7 +190,7 @@ impl MmapInvertedIndex {
|
||||
pub fn filter_has_all<'a>(
|
||||
&'a self,
|
||||
tokens: TokenSet,
|
||||
) -> Box<dyn Iterator<Item = PointOffsetType> + 'a> {
|
||||
) -> Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a> {
|
||||
// in case of mmap immutable index, deleted points are still in the postings
|
||||
let filter = move |idx| self.is_active(idx);
|
||||
|
||||
@@ -198,7 +198,7 @@ impl MmapInvertedIndex {
|
||||
postings: &'a MmapPostings<V>,
|
||||
tokens: TokenSet,
|
||||
filter: impl Fn(u32) -> bool + 'a,
|
||||
) -> Box<dyn Iterator<Item = PointOffsetType> + 'a> {
|
||||
) -> Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a> {
|
||||
let postings_opt: Option<Vec<_>> = tokens
|
||||
.tokens()
|
||||
.iter()
|
||||
@@ -215,10 +215,7 @@ impl MmapInvertedIndex {
|
||||
return Box::new(std::iter::empty());
|
||||
}
|
||||
|
||||
Box::new(intersect_compressed_postings_iterator(
|
||||
posting_readers,
|
||||
filter,
|
||||
))
|
||||
Box::new(intersect_compressed_postings_iterator(posting_readers, filter).map(Ok))
|
||||
}
|
||||
|
||||
match &self.storage.postings {
|
||||
@@ -456,11 +453,11 @@ impl InvertedIndex for MmapInvertedIndex {
|
||||
&'a self,
|
||||
query: ParsedQuery,
|
||||
_hw_counter: &HardwareCounterCell,
|
||||
) -> Box<dyn Iterator<Item = PointOffsetType> + 'a> {
|
||||
) -> Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a> {
|
||||
match query {
|
||||
ParsedQuery::AllTokens(tokens) => self.filter_has_all(tokens),
|
||||
ParsedQuery::Phrase(phrase) => Box::new(self.filter_has_phrase(phrase)),
|
||||
ParsedQuery::AnyTokens(tokens) => Box::new(self.filter_has_any(tokens)),
|
||||
ParsedQuery::Phrase(phrase) => Box::new(self.filter_has_phrase(phrase).map(Ok)),
|
||||
ParsedQuery::AnyTokens(tokens) => Box::new(self.filter_has_any(tokens).map(Ok)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -214,7 +214,7 @@ pub trait InvertedIndex {
|
||||
&'a self,
|
||||
query: ParsedQuery,
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
) -> Box<dyn Iterator<Item = PointOffsetType> + 'a>;
|
||||
) -> Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a>;
|
||||
|
||||
fn get_posting_len(&self, token_id: TokenId, hw_counter: &HardwareCounterCell)
|
||||
-> Option<usize>;
|
||||
@@ -695,10 +695,17 @@ mod tests {
|
||||
// In this case both queries would filter to an empty set of documents.
|
||||
continue;
|
||||
};
|
||||
let mut_filtered = mut_index.filter(mut_query, hw_counter).collect::<Vec<_>>();
|
||||
let imm_filtered = mmap_index.filter(imm_query, hw_counter).collect::<Vec<_>>();
|
||||
let mut_filtered = mut_index
|
||||
.filter(mut_query, hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let imm_filtered = mmap_index
|
||||
.filter(imm_query, hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let imm_mmap_filtered = imm_mmap_index
|
||||
.filter(imm_mmap_query, hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(mut_filtered, imm_filtered);
|
||||
|
||||
+4
-4
@@ -214,11 +214,11 @@ impl InvertedIndex for MutableInvertedIndex {
|
||||
&self,
|
||||
query: ParsedQuery,
|
||||
_hw_counter: &HardwareCounterCell,
|
||||
) -> Box<dyn Iterator<Item = PointOffsetType> + '_> {
|
||||
) -> Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + '_> {
|
||||
match query {
|
||||
ParsedQuery::AllTokens(tokens) => Box::new(self.filter_has_all(tokens)),
|
||||
ParsedQuery::Phrase(phrase) => self.filter_has_phrase(phrase),
|
||||
ParsedQuery::AnyTokens(tokens) => Box::new(self.filter_has_any(tokens)),
|
||||
ParsedQuery::AllTokens(tokens) => Box::new(self.filter_has_all(tokens).map(Ok)),
|
||||
ParsedQuery::Phrase(phrase) => Box::new(self.filter_has_phrase(phrase).map(Ok)),
|
||||
ParsedQuery::AnyTokens(tokens) => Box::new(self.filter_has_any(tokens).map(Ok)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@ mod tests {
|
||||
let search_res: Vec<_> = index
|
||||
.filter(&filter_condition, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
assert_eq!(search_res, vec![0, 4]);
|
||||
|
||||
@@ -352,7 +352,7 @@ mod tests {
|
||||
let search_res: Vec<_> = index
|
||||
.filter(&filter_condition, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
assert_eq!(search_res, vec![2]);
|
||||
|
||||
@@ -360,7 +360,7 @@ mod tests {
|
||||
let search_res: Vec<_> = index
|
||||
.filter(&filter_condition, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
assert_eq!(search_res, vec![4]);
|
||||
|
||||
@@ -372,7 +372,7 @@ mod tests {
|
||||
index
|
||||
.filter(&filter_condition, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.next()
|
||||
.is_none()
|
||||
);
|
||||
@@ -410,7 +410,7 @@ mod tests {
|
||||
let search_res: Vec<_> = index
|
||||
.filter(&filter_condition, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
assert_eq!(search_res, vec![0]);
|
||||
|
||||
@@ -418,7 +418,7 @@ mod tests {
|
||||
let search_res: Vec<_> = index
|
||||
.filter(&filter_condition, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
assert_eq!(search_res, vec![0, 1, 3, 4]);
|
||||
|
||||
@@ -428,7 +428,7 @@ mod tests {
|
||||
let search_res: Vec<_> = index
|
||||
.filter(&filter_condition, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
assert!(search_res.is_empty());
|
||||
assert_eq!(index.count_indexed_points(), 3);
|
||||
@@ -438,7 +438,7 @@ mod tests {
|
||||
let search_res: Vec<_> = index
|
||||
.filter(&filter_condition, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
assert_eq!(search_res, vec![1, 4]);
|
||||
assert_eq!(index.count_indexed_points(), 2);
|
||||
|
||||
@@ -183,7 +183,10 @@ fn test_prefix_search() {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let res: Vec<_> = index.query("ROBO", &hw_counter).collect();
|
||||
let res: Vec<_> = index
|
||||
.query("ROBO", &hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
|
||||
let query = index.parse_text_query("ROBO", &hw_counter).unwrap();
|
||||
|
||||
@@ -193,7 +196,10 @@ fn test_prefix_search() {
|
||||
|
||||
assert_eq!(res.len(), 3);
|
||||
|
||||
let res: Vec<_> = index.query("q231", &hw_counter).collect();
|
||||
let res: Vec<_> = index
|
||||
.query("q231", &hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
assert!(res.is_empty());
|
||||
|
||||
assert!(index.parse_text_query("q231", &hw_counter).is_none());
|
||||
@@ -257,7 +263,10 @@ fn test_phrase_matching() {
|
||||
assert!(index.check_match(&text_query, 1));
|
||||
assert!(index.check_match(&text_query, 2));
|
||||
|
||||
let text_results: Vec<_> = index.filter_query(text_query, &hw_counter).collect();
|
||||
let text_results: Vec<_> = index
|
||||
.filter_query(text_query, &hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
|
||||
// Should match documents 0, 1, and 2 (all contain "quick", "brown", "fox")
|
||||
assert_eq!(text_results.len(), 3);
|
||||
@@ -272,7 +281,10 @@ fn test_phrase_matching() {
|
||||
assert!(index.check_match(&phrase_query, 0));
|
||||
assert!(index.check_match(&phrase_query, 2));
|
||||
|
||||
let phrase_results: Vec<_> = index.filter_query(phrase_query, &hw_counter).collect();
|
||||
let phrase_results: Vec<_> = index
|
||||
.filter_query(phrase_query, &hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
|
||||
// Should only match documents 0 and 2 (contain "quick brown fox" in that exact order)
|
||||
assert_eq!(phrase_results.len(), 2);
|
||||
@@ -284,7 +296,10 @@ fn test_phrase_matching() {
|
||||
let missing_query = index
|
||||
.parse_phrase_query("fox brown quick", &hw_counter)
|
||||
.unwrap();
|
||||
let missing_results: Vec<_> = index.filter_query(missing_query, &hw_counter).collect();
|
||||
let missing_results: Vec<_> = index
|
||||
.filter_query(missing_query, &hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
|
||||
// Should match no documents (no document contains this exact phrase)
|
||||
assert_eq!(missing_results.len(), 0);
|
||||
@@ -301,7 +316,10 @@ fn test_phrase_matching() {
|
||||
assert!(index.check_match(&phrase_query, 4));
|
||||
|
||||
// Should only match document 4
|
||||
let filter_results: Vec<_> = index.filter_query(phrase_query, &hw_counter).collect();
|
||||
let filter_results: Vec<_> = index
|
||||
.filter_query(phrase_query, &hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
assert_eq!(filter_results.len(), 1);
|
||||
assert!(filter_results.contains(&4));
|
||||
};
|
||||
@@ -370,6 +388,7 @@ fn test_ascii_folding_in_full_text_index_word() {
|
||||
|
||||
let results_enabled: Vec<_> = index_enabled
|
||||
.filter_query(query_enabled, &hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
assert!(results_enabled.contains(&0));
|
||||
|
||||
@@ -378,6 +397,7 @@ fn test_ascii_folding_in_full_text_index_word() {
|
||||
if let Some(query_disabled) = query_disabled_opt {
|
||||
let results_disabled: Vec<_> = index_disabled
|
||||
.filter_query(query_disabled, &hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
assert!(!results_disabled.contains(&0));
|
||||
}
|
||||
@@ -387,6 +407,7 @@ fn test_ascii_folding_in_full_text_index_word() {
|
||||
assert!(index_enabled.check_match(&query_acento, 0));
|
||||
let results_acento: Vec<_> = index_enabled
|
||||
.filter_query(query_acento, &hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
assert!(results_acento.contains(&0));
|
||||
|
||||
@@ -395,6 +416,7 @@ fn test_ascii_folding_in_full_text_index_word() {
|
||||
.unwrap();
|
||||
let results_acento2: Vec<_> = index_disabled
|
||||
.filter_query(query_acento2, &hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
assert!(results_acento2.contains(&0));
|
||||
}
|
||||
|
||||
@@ -352,9 +352,11 @@ fn test_congruence(
|
||||
assert_eq!(
|
||||
index_a
|
||||
.filter_query(parsed_query_a, &hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect::<HashSet<_>>(),
|
||||
index_b
|
||||
.filter_query(parsed_query_b, &hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect::<HashSet<_>>(),
|
||||
);
|
||||
}
|
||||
@@ -394,9 +396,11 @@ fn test_congruence(
|
||||
assert_eq!(
|
||||
index_a
|
||||
.filter_query(parsed_query_a, &hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect::<HashSet<_>>(),
|
||||
index_b
|
||||
.filter_query(parsed_query_b, &hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect::<HashSet<_>>(),
|
||||
);
|
||||
}
|
||||
@@ -453,6 +457,7 @@ fn check_phrase<const KEYWORD_COUNT: usize>(
|
||||
|
||||
let result = index
|
||||
.filter_query(parsed_query, &hw_counter)
|
||||
.map(|r| r.unwrap())
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
assert!(!result.is_empty());
|
||||
|
||||
@@ -114,7 +114,7 @@ impl FullTextIndex {
|
||||
&'a self,
|
||||
query: ParsedQuery,
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
) -> Box<dyn Iterator<Item = PointOffsetType> + 'a> {
|
||||
) -> Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a> {
|
||||
match self {
|
||||
Self::Mutable(index) => index.inverted_index.filter(query, hw_counter),
|
||||
Self::Immutable(index) => index.inverted_index.filter(query, hw_counter),
|
||||
@@ -303,7 +303,7 @@ impl FullTextIndex {
|
||||
&'a self,
|
||||
query: &'a str,
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
) -> Box<dyn Iterator<Item = PointOffsetType> + 'a> {
|
||||
) -> Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a> {
|
||||
let Some(parsed_query) = self.parse_text_query(query, hw_counter) else {
|
||||
return Box::new(std::iter::empty());
|
||||
};
|
||||
@@ -469,20 +469,20 @@ impl PayloadFieldIndex for FullTextIndex {
|
||||
&'a self,
|
||||
condition: &'a FieldCondition,
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
) -> OperationResult<Option<Box<dyn Iterator<Item = PointOffsetType> + 'a>>> {
|
||||
) -> Option<Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a>> {
|
||||
let parsed_query_opt = match &condition.r#match {
|
||||
Some(Match::Text(MatchText { text })) => self.parse_text_query(text, hw_counter),
|
||||
Some(Match::Phrase(MatchPhrase { phrase })) => {
|
||||
self.parse_phrase_query(phrase, hw_counter)
|
||||
}
|
||||
_ => return Ok(None),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let Some(parsed_query) = parsed_query_opt else {
|
||||
return Ok(Some(Box::new(std::iter::empty())));
|
||||
return Some(Box::new(std::iter::empty()));
|
||||
};
|
||||
|
||||
Ok(Some(self.filter_query(parsed_query, hw_counter)))
|
||||
Some(self.filter_query(parsed_query, hw_counter))
|
||||
}
|
||||
|
||||
fn estimate_cardinality(
|
||||
|
||||
@@ -228,30 +228,32 @@ impl GeoMapIndex {
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::unnecessary_wraps, reason = "will return Err later")] // FIXME(uio-errors)
|
||||
fn iterator(
|
||||
&self,
|
||||
values: Vec<GeoHash>,
|
||||
) -> OperationResult<Box<dyn Iterator<Item = PointOffsetType> + '_>> {
|
||||
) -> Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + '_> {
|
||||
match self {
|
||||
GeoMapIndex::Mutable(index) => Ok(Box::new(
|
||||
GeoMapIndex::Mutable(index) => Box::new(
|
||||
values
|
||||
.into_iter()
|
||||
.flat_map(|top_geo_hash| index.stored_sub_regions(top_geo_hash))
|
||||
.unique(),
|
||||
)),
|
||||
GeoMapIndex::Immutable(index) => Ok(Box::new(
|
||||
.unique()
|
||||
.map(Ok),
|
||||
),
|
||||
GeoMapIndex::Immutable(index) => Box::new(
|
||||
values
|
||||
.into_iter()
|
||||
.flat_map(|top_geo_hash| index.stored_sub_regions(top_geo_hash))
|
||||
.unique(),
|
||||
)),
|
||||
GeoMapIndex::Mmap(index) => Ok(Box::new(
|
||||
.unique()
|
||||
.map(Ok),
|
||||
),
|
||||
GeoMapIndex::Mmap(index) => Box::new(
|
||||
values
|
||||
.into_iter()
|
||||
.flat_map(|top_geo_hash| index.stored_sub_regions(top_geo_hash))
|
||||
.unique(),
|
||||
)),
|
||||
.unique()
|
||||
.map(Ok),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,51 +525,47 @@ impl PayloadFieldIndex for GeoMapIndex {
|
||||
&'a self,
|
||||
condition: &FieldCondition,
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
) -> OperationResult<Option<Box<dyn Iterator<Item = PointOffsetType> + 'a>>> {
|
||||
) -> Option<Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a>> {
|
||||
if let Some(geo_bounding_box) = &condition.geo_bounding_box {
|
||||
let Some(geo_hashes) = rectangle_hashes(geo_bounding_box, GEO_QUERY_MAX_REGION).ok()
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let geo_hashes = rectangle_hashes(geo_bounding_box, GEO_QUERY_MAX_REGION).ok()?;
|
||||
let geo_condition_copy = *geo_bounding_box;
|
||||
return Ok(Some(Box::new(self.iterator(geo_hashes)?.filter(
|
||||
return Some(Box::new(self.iterator(geo_hashes).filter_map_ok(
|
||||
move |point| {
|
||||
self.check_values_any(*point, hw_counter, |geo_point| {
|
||||
self.check_values_any(point, hw_counter, |geo_point| {
|
||||
geo_condition_copy.check_point(geo_point)
|
||||
})
|
||||
.then_some(point)
|
||||
},
|
||||
))));
|
||||
)));
|
||||
}
|
||||
|
||||
if let Some(geo_radius) = &condition.geo_radius {
|
||||
let Some(geo_hashes) = circle_hashes(geo_radius, GEO_QUERY_MAX_REGION).ok() else {
|
||||
return Ok(None);
|
||||
};
|
||||
let geo_hashes = circle_hashes(geo_radius, GEO_QUERY_MAX_REGION).ok()?;
|
||||
let geo_condition_copy = *geo_radius;
|
||||
return Ok(Some(Box::new(self.iterator(geo_hashes)?.filter(
|
||||
return Some(Box::new(self.iterator(geo_hashes).filter_map_ok(
|
||||
move |point| {
|
||||
self.check_values_any(*point, hw_counter, |geo_point| {
|
||||
self.check_values_any(point, hw_counter, |geo_point| {
|
||||
geo_condition_copy.check_point(geo_point)
|
||||
})
|
||||
.then_some(point)
|
||||
},
|
||||
))));
|
||||
)));
|
||||
}
|
||||
|
||||
if let Some(geo_polygon) = &condition.geo_polygon {
|
||||
let Some(geo_hashes) = polygon_hashes(geo_polygon, GEO_QUERY_MAX_REGION).ok() else {
|
||||
return Ok(None);
|
||||
};
|
||||
let geo_hashes = polygon_hashes(geo_polygon, GEO_QUERY_MAX_REGION).ok()?;
|
||||
let geo_condition_copy = geo_polygon.convert();
|
||||
return Ok(Some(Box::new(self.iterator(geo_hashes)?.filter(
|
||||
return Some(Box::new(self.iterator(geo_hashes).filter_map_ok(
|
||||
move |point| {
|
||||
self.check_values_any(*point, hw_counter, |geo_point| {
|
||||
self.check_values_any(point, hw_counter, |geo_point| {
|
||||
geo_condition_copy.check_point(geo_point)
|
||||
})
|
||||
.then_some(point)
|
||||
},
|
||||
))));
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
None
|
||||
}
|
||||
|
||||
fn estimate_cardinality(
|
||||
@@ -837,7 +835,10 @@ mod tests {
|
||||
index_type: IndexType,
|
||||
) {
|
||||
let (field_index, _, _) = build_random_index(500, 20, index_type);
|
||||
let exact_points_for_hashes = field_index.iterator(hashes).unwrap().collect_vec();
|
||||
let exact_points_for_hashes = field_index
|
||||
.iterator(hashes)
|
||||
.map(|r| r.unwrap())
|
||||
.collect_vec();
|
||||
let real_cardinality = exact_points_for_hashes.len();
|
||||
|
||||
let hw_counter = HardwareCounterCell::new();
|
||||
@@ -911,7 +912,10 @@ mod tests {
|
||||
index_type: IndexType,
|
||||
) {
|
||||
let (field_index, _, _) = build_random_index(500, 20, index_type);
|
||||
let exact_points_for_hashes = field_index.iterator(hashes).unwrap().collect_vec();
|
||||
let exact_points_for_hashes = field_index
|
||||
.iterator(hashes)
|
||||
.map(|r| r.unwrap())
|
||||
.collect_vec();
|
||||
let real_cardinality = exact_points_for_hashes.len();
|
||||
|
||||
let hw_counter = HardwareCounterCell::new();
|
||||
@@ -986,7 +990,7 @@ mod tests {
|
||||
let mut indexed_matched_points = field_index
|
||||
.filter(&field_condition, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect_vec();
|
||||
|
||||
matched_points.sort_unstable();
|
||||
@@ -1052,7 +1056,7 @@ mod tests {
|
||||
let block_points = field_index
|
||||
.filter(&block.condition, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect_vec();
|
||||
assert_eq!(block_points.len(), block.cardinality);
|
||||
});
|
||||
@@ -1260,7 +1264,7 @@ mod tests {
|
||||
let point_offsets = new_index
|
||||
.filter(&field_condition, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect_vec();
|
||||
assert_eq!(point_offsets, vec![1]);
|
||||
|
||||
@@ -1272,7 +1276,7 @@ mod tests {
|
||||
let point_offsets = new_index
|
||||
.filter(&field_condition, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect_vec();
|
||||
assert_eq!(point_offsets, vec![1]);
|
||||
}
|
||||
@@ -1464,7 +1468,7 @@ mod tests {
|
||||
let point_offsets = new_index
|
||||
.filter(&field_condition, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect_vec();
|
||||
// Only LOS_ANGELES is in the bounding box
|
||||
assert_eq!(point_offsets, vec![2]);
|
||||
@@ -1523,7 +1527,7 @@ mod tests {
|
||||
let results = index
|
||||
.filter(&field_condition, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect_vec();
|
||||
assert_eq!(results, vec![1]);
|
||||
|
||||
@@ -1579,7 +1583,7 @@ mod tests {
|
||||
let results = index
|
||||
.filter(&field_condition, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect_vec();
|
||||
assert_eq!(results, vec![0]);
|
||||
|
||||
@@ -1665,11 +1669,11 @@ mod tests {
|
||||
assert_eq!(
|
||||
indices[0]
|
||||
.iterator(hashes.clone())
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect::<HashSet<_>>(),
|
||||
index
|
||||
.iterator(hashes.clone())
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect::<HashSet<_>>(),
|
||||
);
|
||||
for point_id in 0..POINT_COUNT {
|
||||
|
||||
@@ -468,7 +468,7 @@ where
|
||||
&'a self,
|
||||
excluded: &'a IndexSet<K, A>,
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
) -> Box<dyn Iterator<Item = PointOffsetType> + 'a>
|
||||
) -> Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a>
|
||||
where
|
||||
A: BuildHasher,
|
||||
K: Borrow<N> + Hash + Eq,
|
||||
@@ -477,7 +477,8 @@ where
|
||||
self.iter_values()
|
||||
.filter(|key| !excluded.contains((*key).borrow()))
|
||||
.flat_map(move |key| self.get_iterator(key.borrow(), hw_counter))
|
||||
.unique(),
|
||||
.unique()
|
||||
.map(Ok),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -720,12 +721,12 @@ impl PayloadFieldIndex for MapIndex<str> {
|
||||
&'a self,
|
||||
condition: &'a FieldCondition,
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
) -> OperationResult<Option<Box<dyn Iterator<Item = PointOffsetType> + 'a>>> {
|
||||
Ok(match &condition.r#match {
|
||||
) -> Option<Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a>> {
|
||||
match &condition.r#match {
|
||||
Some(Match::Value(MatchValue { value })) => match value {
|
||||
ValueVariants::String(keyword) => {
|
||||
Some(Box::new(self.get_iterator(keyword.as_str(), hw_counter)))
|
||||
}
|
||||
ValueVariants::String(keyword) => Some(Box::new(
|
||||
self.get_iterator(keyword.as_str(), hw_counter).map(Ok),
|
||||
)),
|
||||
ValueVariants::Integer(_) => None,
|
||||
ValueVariants::Bool(_) => None,
|
||||
},
|
||||
@@ -734,7 +735,8 @@ impl PayloadFieldIndex for MapIndex<str> {
|
||||
keywords
|
||||
.iter()
|
||||
.flat_map(move |keyword| self.get_iterator(keyword.as_str(), hw_counter))
|
||||
.unique(),
|
||||
.unique()
|
||||
.map(Ok),
|
||||
)),
|
||||
AnyVariants::Integers(integers) => {
|
||||
if integers.is_empty() {
|
||||
@@ -755,7 +757,7 @@ impl PayloadFieldIndex for MapIndex<str> {
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn estimate_cardinality(
|
||||
@@ -869,34 +871,32 @@ impl PayloadFieldIndex for MapIndex<UuidIntType> {
|
||||
&'a self,
|
||||
condition: &'a FieldCondition,
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
) -> OperationResult<Option<Box<dyn Iterator<Item = PointOffsetType> + 'a>>> {
|
||||
Ok(match &condition.r#match {
|
||||
) -> Option<Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a>> {
|
||||
match &condition.r#match {
|
||||
Some(Match::Value(MatchValue { value })) => match value {
|
||||
ValueVariants::String(uuid_string) => {
|
||||
let Some(uuid) = Uuid::from_str(uuid_string).ok() else {
|
||||
return Ok(None);
|
||||
};
|
||||
Some(Box::new(self.get_iterator(&uuid.as_u128(), hw_counter)))
|
||||
let uuid = Uuid::from_str(uuid_string).ok()?;
|
||||
Some(Box::new(
|
||||
self.get_iterator(&uuid.as_u128(), hw_counter).map(Ok),
|
||||
))
|
||||
}
|
||||
ValueVariants::Integer(_) => None,
|
||||
ValueVariants::Bool(_) => None,
|
||||
},
|
||||
Some(Match::Any(MatchAny { any: any_variant })) => match any_variant {
|
||||
AnyVariants::Strings(uuids_string) => {
|
||||
let uuids: Result<IndexSet<u128>, _> = uuids_string
|
||||
let uuids: IndexSet<u128> = uuids_string
|
||||
.iter()
|
||||
.map(|uuid_string| Uuid::from_str(uuid_string).map(|x| x.as_u128()))
|
||||
.collect();
|
||||
|
||||
let Some(uuids) = uuids.ok() else {
|
||||
return Ok(None);
|
||||
};
|
||||
.collect::<Result<_, _>>()
|
||||
.ok()?;
|
||||
|
||||
Some(Box::new(
|
||||
uuids
|
||||
.into_iter()
|
||||
.flat_map(move |uuid| self.get_iterator(&uuid, hw_counter))
|
||||
.unique(),
|
||||
.unique()
|
||||
.map(Ok),
|
||||
))
|
||||
}
|
||||
AnyVariants::Integers(integers) => {
|
||||
@@ -909,20 +909,18 @@ impl PayloadFieldIndex for MapIndex<UuidIntType> {
|
||||
},
|
||||
Some(Match::Except(MatchExcept { except })) => match except {
|
||||
AnyVariants::Strings(uuids_string) => {
|
||||
let uuids: Result<IndexSet<u128>, _> = uuids_string
|
||||
let excluded_uuids: IndexSet<u128> = uuids_string
|
||||
.iter()
|
||||
.map(|uuid_string| Uuid::from_str(uuid_string).map(|x| x.as_u128()))
|
||||
.collect();
|
||||
|
||||
let Some(excluded_uuids) = uuids.ok() else {
|
||||
return Ok(None);
|
||||
};
|
||||
let exclude_iter = self
|
||||
.iter_values()
|
||||
.filter(move |key| !excluded_uuids.contains(*key))
|
||||
.flat_map(move |key| self.get_iterator(key, hw_counter))
|
||||
.unique();
|
||||
Some(Box::new(exclude_iter))
|
||||
.collect::<Result<_, _>>()
|
||||
.ok()?;
|
||||
Some(Box::new(
|
||||
self.iter_values()
|
||||
.filter(move |key| !excluded_uuids.contains(*key))
|
||||
.flat_map(move |key| self.get_iterator(key, hw_counter))
|
||||
.unique()
|
||||
.map(Ok),
|
||||
))
|
||||
}
|
||||
AnyVariants::Integers(other) => {
|
||||
if other.is_empty() {
|
||||
@@ -933,7 +931,7 @@ impl PayloadFieldIndex for MapIndex<UuidIntType> {
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn estimate_cardinality(
|
||||
@@ -1071,12 +1069,12 @@ impl PayloadFieldIndex for MapIndex<IntPayloadType> {
|
||||
&'a self,
|
||||
condition: &'a FieldCondition,
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
) -> OperationResult<Option<Box<dyn Iterator<Item = PointOffsetType> + 'a>>> {
|
||||
Ok(match &condition.r#match {
|
||||
) -> Option<Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a>> {
|
||||
match &condition.r#match {
|
||||
Some(Match::Value(MatchValue { value })) => match value {
|
||||
ValueVariants::String(_) => None,
|
||||
ValueVariants::Integer(integer) => {
|
||||
Some(Box::new(self.get_iterator(integer, hw_counter)))
|
||||
Some(Box::new(self.get_iterator(integer, hw_counter).map(Ok)))
|
||||
}
|
||||
ValueVariants::Bool(_) => None,
|
||||
},
|
||||
@@ -1092,7 +1090,8 @@ impl PayloadFieldIndex for MapIndex<IntPayloadType> {
|
||||
integers
|
||||
.iter()
|
||||
.flat_map(move |integer| self.get_iterator(integer, hw_counter))
|
||||
.unique(),
|
||||
.unique()
|
||||
.map(Ok),
|
||||
)),
|
||||
},
|
||||
Some(Match::Except(MatchExcept { except })) => match except {
|
||||
@@ -1106,7 +1105,7 @@ impl PayloadFieldIndex for MapIndex<IntPayloadType> {
|
||||
AnyVariants::Integers(integers) => Some(self.except_set(integers, hw_counter)),
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn estimate_cardinality(
|
||||
|
||||
@@ -254,7 +254,7 @@ impl PayloadFieldIndex for MutableNullIndex {
|
||||
&'a self,
|
||||
condition: &'a FieldCondition,
|
||||
_hw_counter: &'a HardwareCounterCell,
|
||||
) -> OperationResult<Option<Box<dyn Iterator<Item = PointOffsetType> + 'a>>> {
|
||||
) -> Option<Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a>> {
|
||||
let FieldCondition {
|
||||
key: _,
|
||||
r#match: _,
|
||||
@@ -267,29 +267,27 @@ impl PayloadFieldIndex for MutableNullIndex {
|
||||
is_null,
|
||||
} = condition;
|
||||
|
||||
Ok(if let Some(is_empty) = is_empty {
|
||||
if let Some(is_empty) = is_empty {
|
||||
if *is_empty {
|
||||
// Return points that don't have values
|
||||
let iter = self.storage.has_values_flags.iter_falses();
|
||||
Some(Box::new(iter))
|
||||
Some(Box::new(
|
||||
self.storage.has_values_flags.iter_falses().map(Ok),
|
||||
))
|
||||
} else {
|
||||
// Return points that have values
|
||||
let iter = self.storage.has_values_flags.iter_trues();
|
||||
Some(Box::new(iter))
|
||||
Some(Box::new(self.storage.has_values_flags.iter_trues().map(Ok)))
|
||||
}
|
||||
} else if let Some(is_null) = is_null {
|
||||
if *is_null {
|
||||
// Return points that have null values
|
||||
let iter = self.storage.is_null_flags.iter_trues();
|
||||
Some(Box::new(iter))
|
||||
Some(Box::new(self.storage.is_null_flags.iter_trues().map(Ok)))
|
||||
} else {
|
||||
// Return points that don't have null values
|
||||
let iter = self.storage.is_null_flags.iter_falses();
|
||||
Some(Box::new(iter))
|
||||
Some(Box::new(self.storage.is_null_flags.iter_falses().map(Ok)))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn estimate_cardinality(
|
||||
@@ -449,12 +447,12 @@ mod tests {
|
||||
let is_null_values: Vec<_> = null_index
|
||||
.filter(&filter_is_null, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
let not_empty_values: Vec<_> = null_index
|
||||
.filter(&filter_is_not_empty, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect();
|
||||
|
||||
let is_empty_values: Vec<_> = (0..n)
|
||||
|
||||
@@ -387,13 +387,19 @@ where
|
||||
&'a self,
|
||||
value: T,
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
) -> Box<dyn Iterator<Item = PointOffsetType> + 'a> {
|
||||
) -> Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a> {
|
||||
let start = Bound::Included(Point::new(value, PointOffsetType::MIN));
|
||||
let end = Bound::Included(Point::new(value, PointOffsetType::MAX));
|
||||
match &self {
|
||||
NumericIndexInner::Mutable(mutable) => Box::new(mutable.values_range(start, end)),
|
||||
NumericIndexInner::Immutable(immutable) => Box::new(immutable.values_range(start, end)),
|
||||
NumericIndexInner::Mmap(mmap) => Box::new(mmap.values_range(start, end, hw_counter)),
|
||||
NumericIndexInner::Mutable(mutable) => {
|
||||
Box::new(mutable.values_range(start, end).map(Ok))
|
||||
}
|
||||
NumericIndexInner::Immutable(immutable) => {
|
||||
Box::new(immutable.values_range(start, end).map(Ok))
|
||||
}
|
||||
NumericIndexInner::Mmap(mmap) => {
|
||||
Box::new(mmap.values_range(start, end, hw_counter).map(Ok))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -764,7 +770,7 @@ where
|
||||
&'a self,
|
||||
condition: &FieldCondition,
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
) -> OperationResult<Option<Box<dyn Iterator<Item = PointOffsetType> + 'a>>> {
|
||||
) -> Option<Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a>> {
|
||||
if let Some(Match::Value(MatchValue {
|
||||
value: ValueVariants::String(keyword),
|
||||
})) = &condition.r#match
|
||||
@@ -773,13 +779,11 @@ where
|
||||
|
||||
if let Ok(uuid) = Uuid::from_str(keyword) {
|
||||
let value = T::from_u128(uuid.as_u128());
|
||||
return Ok(Some(self.point_ids_by_value(value, hw_counter)));
|
||||
return Some(self.point_ids_by_value(value, hw_counter));
|
||||
}
|
||||
}
|
||||
|
||||
let Some(range_cond) = condition.range.as_ref() else {
|
||||
return Ok(None);
|
||||
};
|
||||
let range_cond = condition.range.as_ref()?;
|
||||
|
||||
let (start_bound, end_bound) = match range_cond {
|
||||
RangeInterface::Float(float_range) => float_range.map(|float| T::from_f64(float.0)),
|
||||
@@ -792,20 +796,22 @@ where
|
||||
// map.range
|
||||
// Panics if range start > end. Panics if range start == end and both bounds are Excluded.
|
||||
if !check_boundaries(&start_bound, &end_bound) {
|
||||
return Ok(Some(Box::new(std::iter::empty())));
|
||||
return Some(Box::new(std::iter::empty()));
|
||||
}
|
||||
|
||||
Ok(Some(match self {
|
||||
Some(match self {
|
||||
NumericIndexInner::Mutable(index) => {
|
||||
Box::new(index.values_range(start_bound, end_bound))
|
||||
Box::new(index.values_range(start_bound, end_bound).map(Ok))
|
||||
}
|
||||
NumericIndexInner::Immutable(index) => {
|
||||
Box::new(index.values_range(start_bound, end_bound))
|
||||
Box::new(index.values_range(start_bound, end_bound).map(Ok))
|
||||
}
|
||||
NumericIndexInner::Mmap(index) => {
|
||||
Box::new(index.values_range(start_bound, end_bound, hw_counter))
|
||||
}
|
||||
}))
|
||||
NumericIndexInner::Mmap(index) => Box::new(
|
||||
index
|
||||
.values_range(start_bound, end_bound, hw_counter)
|
||||
.map(Ok),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
fn estimate_cardinality(
|
||||
|
||||
@@ -127,7 +127,7 @@ fn cardinality_request(
|
||||
&hw_counter,
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.unique()
|
||||
.collect_vec();
|
||||
|
||||
@@ -541,7 +541,7 @@ fn test_cond<
|
||||
let offsets = index
|
||||
.filter(&condition, &hw_counter)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.map(|r| r.unwrap())
|
||||
.collect_vec();
|
||||
assert_eq!(offsets, result);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ use common::fs::clear_disk_cache;
|
||||
use common::progress_tracker::ProgressTracker;
|
||||
use common::types::{PointOffsetType, ScoredPointOffset, TelemetryDetail};
|
||||
use fs_err as fs;
|
||||
use itertools::EitherOrBoth;
|
||||
use itertools::{EitherOrBoth, Itertools};
|
||||
use log::{debug, trace};
|
||||
use parking_lot::Mutex;
|
||||
use rand::Rng;
|
||||
@@ -751,7 +751,7 @@ impl HNSWIndex {
|
||||
payload_index.estimate_cardinality(&filter, &disposed_hw_counter)?;
|
||||
let point_mappings = id_tracker.point_mappings();
|
||||
|
||||
Ok(payload_index
|
||||
payload_index
|
||||
.iter_filtered_points(
|
||||
&filter,
|
||||
id_tracker,
|
||||
@@ -761,8 +761,8 @@ impl HNSWIndex {
|
||||
stopped,
|
||||
None,
|
||||
)?
|
||||
.filter(|&point_id| !deleted_bitslice.get_bit(point_id as usize).unwrap_or(false))
|
||||
.collect())
|
||||
.filter_ok(|&point_id| !deleted_bitslice.get_bit(point_id as usize).unwrap_or(false))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -1207,7 +1207,7 @@ impl HNSWIndex {
|
||||
fn search_plain_iterator_batched(
|
||||
&self,
|
||||
query_vectors: &[&QueryVector],
|
||||
points: impl Iterator<Item = PointOffsetType>,
|
||||
points: impl Iterator<Item = OperationResult<PointOffsetType>>,
|
||||
top: usize,
|
||||
params: Option<&SearchParams>,
|
||||
vector_query_context: &VectorQueryContext,
|
||||
@@ -1252,7 +1252,7 @@ impl HNSWIndex {
|
||||
fn search_plain_batched(
|
||||
&self,
|
||||
vectors: &[&QueryVector],
|
||||
filtered_points: impl Iterator<Item = PointOffsetType>,
|
||||
filtered_points: impl Iterator<Item = OperationResult<PointOffsetType>>,
|
||||
top: usize,
|
||||
params: Option<&SearchParams>,
|
||||
vector_query_context: &VectorQueryContext,
|
||||
@@ -1274,7 +1274,7 @@ impl HNSWIndex {
|
||||
vector_query_context: &VectorQueryContext,
|
||||
) -> OperationResult<Vec<Vec<ScoredPointOffset>>> {
|
||||
let id_tracker = self.id_tracker.borrow();
|
||||
let ids_iterator = id_tracker.point_mappings().iter_internal();
|
||||
let ids_iterator = id_tracker.point_mappings().iter_internal().map(Ok);
|
||||
self.search_plain_iterator_batched(vectors, ids_iterator, top, params, vector_query_context)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use common::generic_consts::Random;
|
||||
use common::types::{PointOffsetType, ScoreType, ScoredPointOffset};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::common::operation_error::{CancellableResult, OperationResult, check_process_stopped};
|
||||
use crate::common::operation_error::{OperationResult, check_process_stopped};
|
||||
use crate::data_types::vectors::QueryVector;
|
||||
use crate::payload_storage::FilterContext;
|
||||
use crate::vector_storage::common::VECTOR_READ_BATCH_SIZE;
|
||||
@@ -351,7 +351,7 @@ impl<'a> BatchFilteredSearcher<'a> {
|
||||
self,
|
||||
is_stopped: &AtomicBool,
|
||||
deferred_internal_id: Option<PointOffsetType>,
|
||||
) -> CancellableResult<Vec<Vec<ScoredPointOffset>>> {
|
||||
) -> OperationResult<Vec<Vec<ScoredPointOffset>>> {
|
||||
let iter = self
|
||||
.filters
|
||||
.point_deleted
|
||||
@@ -360,7 +360,8 @@ impl<'a> BatchFilteredSearcher<'a> {
|
||||
.take_while(|&point_id| {
|
||||
// Early exit if we hit the max point ID (e.g. a deferred point).
|
||||
point_id < deferred_internal_id.unwrap_or(PointOffsetType::MAX)
|
||||
});
|
||||
})
|
||||
.map(Ok);
|
||||
|
||||
self.peek_top_iter(iter, is_stopped)
|
||||
}
|
||||
@@ -368,9 +369,9 @@ impl<'a> BatchFilteredSearcher<'a> {
|
||||
/// This function expects deferred points to be already filtered from the iterator.
|
||||
pub fn peek_top_iter(
|
||||
mut self,
|
||||
mut points: impl Iterator<Item = PointOffsetType>,
|
||||
mut points: impl Iterator<Item = OperationResult<PointOffsetType>>,
|
||||
is_stopped: &AtomicBool,
|
||||
) -> CancellableResult<Vec<Vec<ScoredPointOffset>>> {
|
||||
) -> OperationResult<Vec<Vec<ScoredPointOffset>>> {
|
||||
// Reuse the same buffer for all chunks, to avoid reallocation
|
||||
let mut chunk = [0; VECTOR_READ_BATCH_SIZE];
|
||||
let mut scores_buffer = [0.0; VECTOR_READ_BATCH_SIZE];
|
||||
@@ -379,8 +380,9 @@ impl<'a> BatchFilteredSearcher<'a> {
|
||||
check_process_stopped(is_stopped)?;
|
||||
|
||||
let mut chunk_size = 0;
|
||||
for point_id in &mut points {
|
||||
for point_result in &mut points {
|
||||
check_process_stopped(is_stopped)?;
|
||||
let point_id = point_result?;
|
||||
|
||||
if !self.filters.check_vector(point_id) {
|
||||
continue;
|
||||
|
||||
@@ -144,7 +144,8 @@ impl VectorIndex for PlainVectorIndex {
|
||||
&is_stopped,
|
||||
deferred_internal_id,
|
||||
)?;
|
||||
batch_searcher.peek_top_iter(filtered_ids_vec.iter().copied(), &is_stopped)?
|
||||
batch_searcher
|
||||
.peek_top_iter(filtered_ids_vec.iter().copied().map(Ok), &is_stopped)?
|
||||
}
|
||||
None => batch_searcher.peek_top_all(&is_stopped, deferred_internal_id)?,
|
||||
};
|
||||
|
||||
@@ -320,7 +320,7 @@ impl<TInvertedIndex: InvertedIndex> SparseVectorIndex<TInvertedIndex> {
|
||||
let mut results = match filter {
|
||||
Some(filter) => {
|
||||
let payload_index = self.payload_index.borrow();
|
||||
let mut filtered_points = match prefiltered_points {
|
||||
let filtered_points = match prefiltered_points {
|
||||
// `prefiltered_points` always contains visible points only so we don't need additional filtering here.
|
||||
Some(filtered_points) => filtered_points.iter().copied(),
|
||||
None => {
|
||||
@@ -334,7 +334,7 @@ impl<TInvertedIndex: InvertedIndex> SparseVectorIndex<TInvertedIndex> {
|
||||
prefiltered_points.as_ref().unwrap().iter().copied()
|
||||
}
|
||||
};
|
||||
searcher.peek_top_iter(&mut filtered_points, &is_stopped)?
|
||||
searcher.peek_top_iter(filtered_points.map(Ok), &is_stopped)?
|
||||
}
|
||||
None => {
|
||||
searcher.peek_top_all(&is_stopped, vector_query_context.deferred_internal_id())?
|
||||
|
||||
@@ -12,6 +12,7 @@ use common::either_variant::EitherVariant;
|
||||
use common::iterator_ext::IteratorExt;
|
||||
use common::types::PointOffsetType;
|
||||
use fs_err as fs;
|
||||
use itertools::Itertools;
|
||||
use schemars::_serde_json::Value;
|
||||
|
||||
use super::field_index::facet_index::FacetIndexEnum;
|
||||
@@ -100,24 +101,19 @@ impl StructPayloadIndex {
|
||||
&'a self,
|
||||
condition: &'a PrimaryCondition,
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
) -> OperationResult<Option<Box<dyn Iterator<Item = PointOffsetType> + 'a>>> {
|
||||
) -> Option<Box<dyn Iterator<Item = OperationResult<PointOffsetType>> + 'a>> {
|
||||
match condition {
|
||||
PrimaryCondition::Condition(field_condition) => {
|
||||
let field_key = &field_condition.key;
|
||||
let Some(field_indexes) = self.field_indexes.get(field_key) else {
|
||||
return Ok(None);
|
||||
};
|
||||
let field_indexes = self.field_indexes.get(field_key)?;
|
||||
field_indexes
|
||||
.iter()
|
||||
.find_map(|field_index| {
|
||||
field_index.filter(field_condition, hw_counter).transpose()
|
||||
})
|
||||
.transpose()
|
||||
.find_map(|field_index| field_index.filter(field_condition, hw_counter))
|
||||
}
|
||||
PrimaryCondition::Ids(ids) => {
|
||||
Ok(Some(Box::new(ids.resolved_point_offsets.iter().copied())))
|
||||
Some(Box::new(ids.resolved_point_offsets.iter().copied().map(Ok)))
|
||||
}
|
||||
PrimaryCondition::HasVector(_) => Ok(None),
|
||||
PrimaryCondition::HasVector(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,7 +478,7 @@ impl StructPayloadIndex {
|
||||
hw_counter: &'a HardwareCounterCell,
|
||||
is_stopped: &'a AtomicBool,
|
||||
deferred_internal_id: Option<PointOffsetType>,
|
||||
) -> OperationResult<impl Iterator<Item = PointOffsetType> + 'a> {
|
||||
) -> OperationResult<impl Iterator<Item = OperationResult<PointOffsetType>> + 'a> {
|
||||
if query_cardinality.primary_clauses.is_empty() {
|
||||
let full_scan_iterator = point_mappings.iter_internal_visible(deferred_internal_id);
|
||||
|
||||
@@ -490,7 +486,8 @@ impl StructPayloadIndex {
|
||||
// Worst case: query expected to return few matches, but index can't be used
|
||||
let matched_points = full_scan_iterator
|
||||
.stop_if(is_stopped)
|
||||
.filter(move |i| struct_filtered_context.check(*i));
|
||||
.filter(move |i| struct_filtered_context.check(*i))
|
||||
.map(Ok);
|
||||
|
||||
Ok(EitherVariant::A(matched_points))
|
||||
} else {
|
||||
@@ -503,7 +500,7 @@ impl StructPayloadIndex {
|
||||
.primary_clauses
|
||||
.iter()
|
||||
.map(|clause| self.query_field(clause, hw_counter))
|
||||
.collect::<OperationResult<_>>()?;
|
||||
.collect();
|
||||
|
||||
if let Some(primary_iterators) = primary_clause_iterators {
|
||||
let all_conditions_are_primary = filter
|
||||
@@ -515,7 +512,7 @@ impl StructPayloadIndex {
|
||||
// Filter out deferred points.
|
||||
// This iterator (and each primary iterator too) can yield items in non sorted order, depending on the type of index and primary condition.
|
||||
.flatten()
|
||||
.filter(move |&internal_id| {
|
||||
.filter_ok(move |&internal_id| {
|
||||
internal_id < deferred_internal_id.unwrap_or(PointOffsetType::MAX)
|
||||
})
|
||||
.stop_if(is_stopped);
|
||||
@@ -524,13 +521,13 @@ impl StructPayloadIndex {
|
||||
// All conditions are primary clauses,
|
||||
// We can avoid post-filtering
|
||||
let iter = joined_primary_iterator
|
||||
.filter(move |&id| !visited_list.check_and_update_visited(id));
|
||||
.filter_ok(move |&id| !visited_list.check_and_update_visited(id));
|
||||
EitherVariant::B(iter)
|
||||
} else {
|
||||
// Some conditions are primary clauses, some are not
|
||||
let struct_filtered_context =
|
||||
self.struct_filtered_context(filter, hw_counter)?;
|
||||
let iter = joined_primary_iterator.filter(move |&id| {
|
||||
let iter = joined_primary_iterator.filter_ok(move |&id| {
|
||||
!visited_list.check_and_update_visited(id)
|
||||
&& struct_filtered_context.check(id)
|
||||
});
|
||||
@@ -551,7 +548,8 @@ impl StructPayloadIndex {
|
||||
})
|
||||
.filter(move |&id| {
|
||||
!visited_list.check_and_update_visited(id) && struct_filtered_context.check(id)
|
||||
});
|
||||
})
|
||||
.map(Ok);
|
||||
|
||||
Ok(EitherVariant::D(iter))
|
||||
}
|
||||
@@ -781,17 +779,16 @@ impl PayloadIndex for StructPayloadIndex {
|
||||
let query_cardinality = self.estimate_cardinality(filter, hw_counter)?;
|
||||
let id_tracker = self.id_tracker.borrow();
|
||||
let point_mappings = id_tracker.point_mappings();
|
||||
Ok(self
|
||||
.iter_filtered_points(
|
||||
filter,
|
||||
&id_tracker,
|
||||
&point_mappings,
|
||||
&query_cardinality,
|
||||
hw_counter,
|
||||
is_stopped,
|
||||
deferred_internal_id,
|
||||
)?
|
||||
.collect())
|
||||
self.iter_filtered_points(
|
||||
filter,
|
||||
&id_tracker,
|
||||
&point_mappings,
|
||||
&query_cardinality,
|
||||
hw_counter,
|
||||
is_stopped,
|
||||
deferred_internal_id,
|
||||
)?
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn indexed_points(&self, field: PayloadKeyTypeRef) -> usize {
|
||||
|
||||
@@ -62,16 +62,20 @@ impl Segment {
|
||||
is_stopped,
|
||||
self.deferred_internal_id(),
|
||||
)?
|
||||
.filter(|point_id| !id_tracker.is_deleted_point(*point_id))
|
||||
.fold(HashMap::new(), |mut map, point_id| {
|
||||
facet_index
|
||||
.get_point_values(point_id, hw_counter)
|
||||
.unique()
|
||||
.for_each(|value| {
|
||||
*map.entry(value).or_insert(0) += 1;
|
||||
});
|
||||
map
|
||||
})
|
||||
.filter_ok(|&point_id| !id_tracker.is_deleted_point(point_id))
|
||||
.try_fold(
|
||||
HashMap::new(),
|
||||
|mut map, point_result| -> OperationResult<_> {
|
||||
let point_id = point_result?;
|
||||
facet_index
|
||||
.get_point_values(point_id, hw_counter)
|
||||
.unique()
|
||||
.for_each(|value| {
|
||||
*map.entry(value).or_insert(0) += 1;
|
||||
});
|
||||
Ok(map)
|
||||
},
|
||||
)?
|
||||
.into_iter()
|
||||
.map(|(value, count)| FacetHit { value, count });
|
||||
|
||||
@@ -159,11 +163,15 @@ impl Segment {
|
||||
is_stopped,
|
||||
self.deferred_internal_id(),
|
||||
)?
|
||||
.filter(|point_id| !id_tracker.is_deleted_point(*point_id))
|
||||
.fold(BTreeSet::new(), |mut set, point_id| {
|
||||
set.extend(facet_index.get_point_values(point_id, hw_counter));
|
||||
set
|
||||
})
|
||||
.filter_ok(|&point_id| !id_tracker.is_deleted_point(point_id))
|
||||
.try_fold(
|
||||
BTreeSet::new(),
|
||||
|mut set, point_result| -> OperationResult<_> {
|
||||
let point_id = point_result?;
|
||||
set.extend(facet_index.get_point_values(point_id, hw_counter));
|
||||
Ok(set)
|
||||
},
|
||||
)?
|
||||
.into_iter()
|
||||
.map(|value| value.to_owned())
|
||||
.collect()
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::sync::atomic::AtomicBool;
|
||||
use common::counter::hardware_counter::HardwareCounterCell;
|
||||
use common::iterator_ext::IteratorExt;
|
||||
use common::types::{DeferredBehavior, PointOffsetType};
|
||||
use itertools::Either;
|
||||
use itertools::{Either, Itertools};
|
||||
|
||||
use super::Segment;
|
||||
use crate::common::operation_error::{OperationError, OperationResult};
|
||||
@@ -53,24 +53,27 @@ impl Segment {
|
||||
is_stopped,
|
||||
effective_deferred_id,
|
||||
)?
|
||||
.flat_map(|internal_id| {
|
||||
// Repeat a point for as many values as it has
|
||||
numeric_index
|
||||
.get_ordering_values(internal_id)
|
||||
// But only those which start from `start_from`
|
||||
.filter(|value| match order_by.direction() {
|
||||
Direction::Asc => value >= &start_from,
|
||||
Direction::Desc => value <= &start_from,
|
||||
})
|
||||
.map(move |ordering_value| (ordering_value, internal_id))
|
||||
.flat_map(|point_result| match point_result {
|
||||
Err(e) => Either::Left(std::iter::once(Err(e))),
|
||||
Ok(internal_id) => Either::Right(
|
||||
// Repeat a point for as many values as it has
|
||||
numeric_index
|
||||
.get_ordering_values(internal_id)
|
||||
// But only those which start from `start_from`
|
||||
.filter(|value| match order_by.direction() {
|
||||
Direction::Asc => value >= &start_from,
|
||||
Direction::Desc => value <= &start_from,
|
||||
})
|
||||
.map(move |ordering_value| Ok((ordering_value, internal_id))),
|
||||
),
|
||||
})
|
||||
.filter_map(|(value, internal_id)| {
|
||||
.filter_map_ok(|(value, internal_id)| {
|
||||
id_tracker
|
||||
.external_id(internal_id)
|
||||
.map(|external_id| (value, external_id))
|
||||
});
|
||||
|
||||
let page = match order_by.direction() {
|
||||
values_ids_iterator.process_results(|values_ids_iterator| match order_by.direction() {
|
||||
Direction::Asc => {
|
||||
let mut page = match limit {
|
||||
Some(limit) => peek_top_smallest_iterable(values_ids_iterator, limit),
|
||||
@@ -87,9 +90,7 @@ impl Segment {
|
||||
page.sort_unstable_by_key(|(value, _)| Reverse(*value));
|
||||
page
|
||||
}
|
||||
};
|
||||
|
||||
Ok(page)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn filtered_read_by_value_stream(
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::sync::atomic::AtomicBool;
|
||||
|
||||
use common::counter::hardware_counter::HardwareCounterCell;
|
||||
use common::iterator_ext::IteratorExt;
|
||||
use itertools::Itertools;
|
||||
use rand::seq::{IteratorRandom, SliceRandom};
|
||||
|
||||
use super::Segment;
|
||||
@@ -33,13 +34,14 @@ impl Segment {
|
||||
is_stopped,
|
||||
self.deferred_internal_id(),
|
||||
)?
|
||||
.filter_map(|internal_id| id_tracker.external_id(internal_id));
|
||||
.filter_map_ok(|internal_id| id_tracker.external_id(internal_id));
|
||||
|
||||
let mut rng = rand::rng();
|
||||
let mut shuffled = ids_iterator.sample(&mut rng, limit);
|
||||
shuffled.shuffle(&mut rng);
|
||||
|
||||
Ok(shuffled)
|
||||
ids_iterator.process_results(|iter| {
|
||||
let mut shuffled = iter.sample(&mut rng, limit);
|
||||
shuffled.shuffle(&mut rng);
|
||||
shuffled
|
||||
})
|
||||
}
|
||||
|
||||
pub fn filtered_read_by_random_stream(
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::sync::atomic::AtomicBool;
|
||||
use common::counter::hardware_counter::HardwareCounterCell;
|
||||
use common::iterator_ext::IteratorExt;
|
||||
use common::types::DeferredBehavior;
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::Segment;
|
||||
use crate::common::operation_error::OperationResult;
|
||||
@@ -129,24 +130,21 @@ impl Segment {
|
||||
is_stopped,
|
||||
effective_deferred_id,
|
||||
)?
|
||||
.filter_map(|internal_id| {
|
||||
let external_id = id_tracker.external_id(internal_id);
|
||||
match external_id {
|
||||
Some(external_id) => match offset {
|
||||
Some(offset) if external_id < offset => None,
|
||||
_ => Some(external_id),
|
||||
},
|
||||
None => None,
|
||||
.filter_map_ok(|internal_id| {
|
||||
let external_id = id_tracker.external_id(internal_id)?;
|
||||
match offset {
|
||||
Some(offset) if external_id < offset => None,
|
||||
_ => Some(external_id),
|
||||
}
|
||||
});
|
||||
|
||||
let mut page = match limit {
|
||||
Some(limit) => peek_top_smallest_iterable(ids_iterator, limit),
|
||||
None => ids_iterator.collect(),
|
||||
};
|
||||
|
||||
page.sort_unstable();
|
||||
|
||||
Ok(page)
|
||||
ids_iterator.process_results(|iter| {
|
||||
let mut page = match limit {
|
||||
Some(limit) => peek_top_smallest_iterable(iter, limit),
|
||||
None => iter.collect(),
|
||||
};
|
||||
page.sort_unstable();
|
||||
page
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,7 +504,7 @@ mod tests {
|
||||
2,
|
||||
);
|
||||
let res = searcher
|
||||
.peek_top_iter(&mut [0, 1, 2, 3, 4].iter().cloned(), &DEFAULT_STOPPED)
|
||||
.peek_top_iter([0, 1, 2, 3, 4].iter().cloned().map(Ok), &DEFAULT_STOPPED)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.exactly_one()
|
||||
@@ -576,7 +576,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let closest = searcher
|
||||
.peek_top_iter(&mut [0, 1, 2, 3, 4].iter().cloned(), &DEFAULT_STOPPED)
|
||||
.peek_top_iter([0, 1, 2, 3, 4].iter().cloned().map(Ok), &DEFAULT_STOPPED)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.exactly_one()
|
||||
@@ -605,7 +605,7 @@ mod tests {
|
||||
5,
|
||||
);
|
||||
let closest = searcher
|
||||
.peek_top_iter(&mut [0, 1, 2, 3, 4].iter().cloned(), &DEFAULT_STOPPED)
|
||||
.peek_top_iter([0, 1, 2, 3, 4].iter().cloned().map(Ok), &DEFAULT_STOPPED)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.exactly_one()
|
||||
@@ -694,7 +694,7 @@ mod tests {
|
||||
5,
|
||||
);
|
||||
let closest = searcher
|
||||
.peek_top_iter(&mut [0, 1, 2, 3, 4].iter().cloned(), &DEFAULT_STOPPED)
|
||||
.peek_top_iter([0, 1, 2, 3, 4].iter().cloned().map(Ok), &DEFAULT_STOPPED)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.exactly_one()
|
||||
|
||||
@@ -62,7 +62,7 @@ fn do_test_delete_points(storage: &mut VectorStorageEnum) {
|
||||
5,
|
||||
);
|
||||
let closest = searcher
|
||||
.peek_top_iter(&mut [0, 1, 2, 3, 4].iter().cloned(), &DEFAULT_STOPPED)
|
||||
.peek_top_iter([0, 1, 2, 3, 4].iter().cloned().map(Ok), &DEFAULT_STOPPED)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.exactly_one()
|
||||
@@ -90,7 +90,7 @@ fn do_test_delete_points(storage: &mut VectorStorageEnum) {
|
||||
5,
|
||||
);
|
||||
let closest = searcher
|
||||
.peek_top_iter(&mut [0, 1, 2, 3, 4].iter().cloned(), &DEFAULT_STOPPED)
|
||||
.peek_top_iter([0, 1, 2, 3, 4].iter().cloned().map(Ok), &DEFAULT_STOPPED)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.exactly_one()
|
||||
@@ -175,7 +175,7 @@ fn do_test_update_from_delete_points(storage: &mut VectorStorageEnum) {
|
||||
5,
|
||||
);
|
||||
let closest = searcher
|
||||
.peek_top_iter(&mut [0, 1, 2, 3, 4].iter().cloned(), &DEFAULT_STOPPED)
|
||||
.peek_top_iter([0, 1, 2, 3, 4].iter().cloned().map(Ok), &DEFAULT_STOPPED)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.exactly_one()
|
||||
@@ -223,7 +223,7 @@ fn do_test_score_points(storage: &mut VectorStorageEnum) {
|
||||
2,
|
||||
);
|
||||
let closest = searcher
|
||||
.peek_top_iter(&mut [0, 1, 2, 3, 4].iter().cloned(), &DEFAULT_STOPPED)
|
||||
.peek_top_iter([0, 1, 2, 3, 4].iter().cloned().map(Ok), &DEFAULT_STOPPED)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.exactly_one()
|
||||
@@ -262,7 +262,7 @@ fn do_test_score_points(storage: &mut VectorStorageEnum) {
|
||||
)
|
||||
.unwrap();
|
||||
let closest = searcher
|
||||
.peek_top_iter(&mut [0, 1, 2, 3, 4].iter().cloned(), &DEFAULT_STOPPED)
|
||||
.peek_top_iter([0, 1, 2, 3, 4].iter().cloned().map(Ok), &DEFAULT_STOPPED)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.exactly_one()
|
||||
|
||||
@@ -129,7 +129,7 @@ fn do_test_delete_points(vector_dim: usize, vec_count: usize, storage: &mut Vect
|
||||
5,
|
||||
);
|
||||
let closest = searcher
|
||||
.peek_top_iter(&mut [0, 1, 2, 3, 4].iter().cloned(), &DEFAULT_STOPPED)
|
||||
.peek_top_iter([0, 1, 2, 3, 4].iter().cloned().map(Ok), &DEFAULT_STOPPED)
|
||||
.unwrap()
|
||||
.pop()
|
||||
.unwrap();
|
||||
@@ -156,7 +156,7 @@ fn do_test_delete_points(vector_dim: usize, vec_count: usize, storage: &mut Vect
|
||||
5,
|
||||
);
|
||||
let closest = searcher
|
||||
.peek_top_iter(&mut [0, 1, 2, 3, 4].iter().cloned(), &DEFAULT_STOPPED)
|
||||
.peek_top_iter([0, 1, 2, 3, 4].iter().cloned().map(Ok), &DEFAULT_STOPPED)
|
||||
.unwrap()
|
||||
.pop()
|
||||
.unwrap();
|
||||
@@ -244,7 +244,7 @@ fn do_test_update_from_delete_points(
|
||||
5,
|
||||
);
|
||||
let closest = searcher
|
||||
.peek_top_iter(&mut [0, 1, 2, 3, 4].iter().cloned(), &DEFAULT_STOPPED)
|
||||
.peek_top_iter([0, 1, 2, 3, 4].iter().cloned().map(Ok), &DEFAULT_STOPPED)
|
||||
.unwrap()
|
||||
.pop()
|
||||
.unwrap();
|
||||
|
||||
@@ -80,7 +80,7 @@ fn do_test_delete_points(storage: &mut VectorStorageEnum) {
|
||||
5,
|
||||
);
|
||||
let closest = searcher
|
||||
.peek_top_iter(&mut [0, 1, 2, 3, 4].iter().cloned(), &DEFAULT_STOPPED)
|
||||
.peek_top_iter([0, 1, 2, 3, 4].iter().cloned().map(Ok), &DEFAULT_STOPPED)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.exactly_one()
|
||||
@@ -172,7 +172,7 @@ fn do_test_update_from_delete_points(storage: &mut VectorStorageEnum) {
|
||||
5,
|
||||
);
|
||||
let results = searcher
|
||||
.peek_top_iter(&mut [0, 1, 2, 3, 4, 5].iter().cloned(), &DEFAULT_STOPPED)
|
||||
.peek_top_iter([0, 1, 2, 3, 4, 5].iter().cloned().map(Ok), &DEFAULT_STOPPED)
|
||||
.unwrap();
|
||||
|
||||
let closest = results.into_iter().exactly_one().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user