From d64178906fca60361b1784d7e8c676471433b1bf Mon Sep 17 00:00:00 2001 From: Paul Masurel Date: Tue, 20 Jan 2026 10:16:10 +0100 Subject: [PATCH] blop --- src/codec/mod.rs | 5 +- .../standard/postings/segment_postings.rs | 12 +- src/index/segment_reader.rs | 2 +- src/postings/mod.rs | 2 +- src/postings/postings.rs | 71 +++---- src/query/boolean_query/boolean_weight.rs | 187 ++++-------------- src/query/phrase_query/phrase_weight.rs | 8 +- src/query/scorer.rs | 45 ++++- src/query/term_query/term_scorer.rs | 6 +- src/query/term_query/term_weight.rs | 10 +- src/query/union/buffered_union.rs | 31 ++- src/query/weight.rs | 28 +-- 12 files changed, 162 insertions(+), 245 deletions(-) diff --git a/src/codec/mod.rs b/src/codec/mod.rs index 70e753e50..ae302d3b3 100644 --- a/src/codec/mod.rs +++ b/src/codec/mod.rs @@ -9,10 +9,11 @@ pub use standard::StandardCodec; use crate::codec::postings::PostingsCodec; use crate::fieldnorm::FieldNormReader; -use crate::postings::{Postings, TermInfo}; +use crate::postings::{Postings, PostingsWithBlockMax, TermInfo}; +use crate::query::term_query::TermScorer; use crate::query::{Bm25Weight, Scorer}; use crate::schema::IndexRecordOption; -use crate::InvertedIndexReader; +use crate::{DocId, InvertedIndexReader, Score, SegmentReader}; pub trait Codec: Clone + std::fmt::Debug + Send + Sync + 'static { type PostingsCodec: PostingsCodec; diff --git a/src/codec/standard/postings/segment_postings.rs b/src/codec/standard/postings/segment_postings.rs index 176cc2691..6b8d11f71 100644 --- a/src/codec/standard/postings/segment_postings.rs +++ b/src/codec/standard/postings/segment_postings.rs @@ -6,7 +6,7 @@ use crate::docset::DocSet; use crate::fieldnorm::FieldNormReader; use crate::positions::PositionReader; use crate::postings::compression::COMPRESSION_BLOCK_SIZE; -use crate::postings::Postings; +use crate::postings::{Postings, PostingsWithBlockMax}; use crate::query::Bm25Weight; use crate::{DocId, Score}; @@ -249,10 +249,12 @@ impl Postings for SegmentPostings { } } - fn supports_block_max(&self) -> bool { - true + fn has_freq(&self) -> bool { + !self.block_cursor.freqs().is_empty() } +} +impl PostingsWithBlockMax for SegmentPostings { fn seek_block( &mut self, target_doc: crate::DocId, @@ -267,10 +269,6 @@ impl Postings for SegmentPostings { fn last_doc_in_block(&self) -> crate::DocId { self.block_cursor.skip_reader().last_doc_in_block() } - - fn has_freq(&self) -> bool { - self.block_cursor.freq_reading_option() == FreqReadingOption::ReadFreq - } } #[cfg(test)] diff --git a/src/index/segment_reader.rs b/src/index/segment_reader.rs index 0de9e072d..8915aa941 100644 --- a/src/index/segment_reader.rs +++ b/src/index/segment_reader.rs @@ -49,7 +49,7 @@ pub struct SegmentReader { alive_bitset_opt: Option, schema: Schema, - codec: Arc, + pub(crate) codec: Arc, } impl SegmentReader { diff --git a/src/postings/mod.rs b/src/postings/mod.rs index 311848d10..29c075a49 100644 --- a/src/postings/mod.rs +++ b/src/postings/mod.rs @@ -21,7 +21,7 @@ pub(crate) use stacker::compute_table_memory_size; pub(crate) use self::indexing_context::IndexingContext; pub(crate) use self::per_field_postings_writer::PerFieldPostingsWriter; -pub use self::postings::Postings; +pub use self::postings::{Postings, PostingsWithBlockMax}; pub(crate) use self::postings_writer::{ serialize_postings, IndexingPosition, PostingsWriter, PostingsWriterEnum, }; diff --git a/src/postings/postings.rs b/src/postings/postings.rs index 94da7c834..97a6ee22b 100644 --- a/src/postings/postings.rs +++ b/src/postings/postings.rs @@ -1,6 +1,6 @@ use crate::docset::DocSet; use crate::fieldnorm::FieldNormReader; -use crate::query::{Bm25Weight, Scorer}; +use crate::query::Bm25Weight; use crate::Score; /// Postings (also called inverted list) @@ -41,28 +41,6 @@ pub trait Postings: DocSet + 'static { } fn has_freq(&self) -> bool; - - // TODO see if we can put that in a lift to PostingsWithBlockMax trait. - // supports Block-Wand - fn supports_block_max(&self) -> bool { - false - } - // TODO document - // Only allowed for block max. - fn seek_block( - &mut self, - _target_doc: crate::DocId, - _fieldnorm_reader: &FieldNormReader, - _similarity_weight: &Bm25Weight, - ) -> Score { - unimplemented!() - } - - // TODO - // Only allowed for block max. - fn last_doc_in_block(&self) -> crate::DocId { - unimplemented!() - } } impl Postings for Box { @@ -74,10 +52,45 @@ impl Postings for Box { (**self).append_positions_with_offset(offset, output); } - fn supports_block_max(&self) -> bool { - (**self).supports_block_max() + fn has_freq(&self) -> bool { + (**self).has_freq() } + fn doc_freq(&self) -> u32 { + (**self).doc_freq() + } +} + +impl Postings for Box { + fn term_freq(&self) -> u32 { + (**self).term_freq() + } + + fn append_positions_with_offset(&mut self, offset: u32, output: &mut Vec) { + (**self).append_positions_with_offset(offset, output); + } + + fn has_freq(&self) -> bool { + (**self).has_freq() + } + + fn doc_freq(&self) -> u32 { + (**self).doc_freq() + } +} + +pub trait PostingsWithBlockMax: Postings { + fn seek_block( + &mut self, + target_doc: crate::DocId, + fieldnorm_reader: &FieldNormReader, + similarity_weight: &Bm25Weight, + ) -> Score; + + fn last_doc_in_block(&self) -> crate::DocId; +} + +impl PostingsWithBlockMax for Box { fn seek_block( &mut self, target_doc: crate::DocId, @@ -90,12 +103,4 @@ impl Postings for Box { fn last_doc_in_block(&self) -> crate::DocId { (**self).last_doc_in_block() } - - fn has_freq(&self) -> bool { - (**self).has_freq() - } - - fn doc_freq(&self) -> u32 { - (**self).doc_freq() - } } diff --git a/src/query/boolean_query/boolean_weight.rs b/src/query/boolean_query/boolean_weight.rs index a666f3e9d..66b8474a4 100644 --- a/src/query/boolean_query/boolean_weight.rs +++ b/src/query/boolean_query/boolean_weight.rs @@ -5,19 +5,13 @@ use crate::index::SegmentReader; use crate::query::disjunction::Disjunction; use crate::query::explanation::does_not_match; use crate::query::score_combiner::{DoNothingCombiner, ScoreCombiner}; -use crate::query::term_query::TermScorer; -use crate::query::weight::{for_each_docset_buffered, for_each_pruning_scorer, for_each_scorer}; +use crate::query::weight::{for_each_docset_buffered, for_each_scorer}; use crate::query::{ intersect_scorers, AllScorer, BufferedUnionScorer, EmptyScorer, Exclude, Explanation, Occur, RequiredOptionalScorer, Scorer, Weight, }; use crate::{DocId, Score}; -enum SpecializedScorer { - TermUnion(Vec), - Other(Box), -} - fn scorer_disjunction( scorers: Vec>, score_combiner: TScoreCombiner, @@ -43,53 +37,18 @@ fn scorer_union( scorers: Vec>, score_combiner_fn: impl Fn() -> TScoreCombiner, num_docs: u32, -) -> SpecializedScorer +) -> Box where TScoreCombiner: ScoreCombiner, { - assert!(!scorers.is_empty()); - if scorers.len() == 1 { - return SpecializedScorer::Other(scorers.into_iter().next().unwrap()); //< we checked the size beforehand - } - - { - let is_all_term_queries = scorers.iter().all(|scorer| scorer.is::()); - if is_all_term_queries { - let scorers: Vec = scorers - .into_iter() - .map(|scorer| *(scorer.downcast::().map_err(|_| ()).unwrap())) - .collect(); - if scorers.iter().all(|scorer| scorer.has_freq()) { - // Block wand is only available if we read frequencies. - return SpecializedScorer::TermUnion(scorers); - } else { - return SpecializedScorer::Other(Box::new(BufferedUnionScorer::build( - scorers, - score_combiner_fn, - num_docs, - ))); - } - } - } - SpecializedScorer::Other(Box::new(BufferedUnionScorer::build( - scorers, - score_combiner_fn, - num_docs, - ))) -} - -fn into_box_scorer( - scorer: SpecializedScorer, - score_combiner_fn: impl Fn() -> TScoreCombiner, - num_docs: u32, -) -> Box { - match scorer { - SpecializedScorer::TermUnion(term_scorers) => { - let union_scorer = - BufferedUnionScorer::build(term_scorers, score_combiner_fn, num_docs); - Box::new(union_scorer) - } - SpecializedScorer::Other(scorer) => scorer, + match scorers.len() { + 0 => Box::new(EmptyScorer), + 1 => Box::new(scorers.into_iter().next().unwrap()), + _ => Box::new(BufferedUnionScorer::build( + scorers, + score_combiner_fn, + num_docs, + )), } } @@ -124,28 +83,26 @@ fn effective_must_scorer( /// When `scoring_enabled` is false, we can just return AllScorer alone since /// we don't need score contributions from the should_scorer. fn effective_should_scorer_for_union( - should_scorer: SpecializedScorer, + should_scorer: Box, removed_all_scorer_count: usize, max_doc: DocId, num_docs: u32, score_combiner_fn: impl Fn() -> TScoreCombiner, scoring_enabled: bool, -) -> SpecializedScorer { +) -> Box { if removed_all_scorer_count > 0 { if scoring_enabled { // Need to union to get score contributions from both - let all_scorers: Vec> = vec![ - into_box_scorer(should_scorer, &score_combiner_fn, num_docs), - Box::new(AllScorer::new(max_doc)), - ]; - SpecializedScorer::Other(Box::new(BufferedUnionScorer::build( + let all_scorers: Vec> = + vec![should_scorer, Box::new(AllScorer::new(max_doc))]; + Box::new(BufferedUnionScorer::build( all_scorers, score_combiner_fn, num_docs, - ))) + )) } else { // Scoring disabled - AllScorer alone is sufficient - SpecializedScorer::Other(Box::new(AllScorer::new(max_doc))) + Box::new(AllScorer::new(max_doc)) } } else { should_scorer @@ -156,9 +113,9 @@ enum ShouldScorersCombinationMethod { // Should scorers are irrelevant. Ignored, // Only contributes to final score. - Optional(SpecializedScorer), + Optional(Box), // Regardless of score, the should scorers may impact whether a document is matching or not. - Required(SpecializedScorer), + Required(Box), } /// Weight associated to the `BoolQuery`. @@ -220,7 +177,7 @@ impl BooleanWeight { reader: &SegmentReader, boost: Score, score_combiner_fn: impl Fn() -> TComplexScoreCombiner, - ) -> crate::Result { + ) -> crate::Result> { let num_docs = reader.num_docs(); let mut per_occur_scorers = self.per_occur_scorers(reader, boost)?; @@ -230,7 +187,7 @@ impl BooleanWeight { let must_special_scorer_counts = remove_and_count_all_and_empty_scorers(&mut must_scorers); if must_special_scorer_counts.num_empty_scorers > 0 { - return Ok(SpecializedScorer::Other(Box::new(EmptyScorer))); + return Ok(Box::new(EmptyScorer)); } let mut should_scorers = per_occur_scorers.remove(&Occur::Should).unwrap_or_default(); @@ -245,7 +202,7 @@ impl BooleanWeight { if exclude_special_scorer_counts.num_all_scorers > 0 { // We exclude all documents at one point. - return Ok(SpecializedScorer::Other(Box::new(EmptyScorer))); + return Ok(Box::new(EmptyScorer)); } let effective_minimum_number_should_match = self @@ -257,7 +214,7 @@ impl BooleanWeight { if effective_minimum_number_should_match > num_of_should_scorers { // We don't have enough scorers to satisfy the minimum number of should matches. // The request will match no documents. - return Ok(SpecializedScorer::Other(Box::new(EmptyScorer))); + return Ok(Box::new(EmptyScorer)); } match effective_minimum_number_should_match { 0 if num_of_should_scorers == 0 => ShouldScorersCombinationMethod::Ignored, @@ -277,12 +234,10 @@ impl BooleanWeight { must_scorers.append(&mut should_scorers); ShouldScorersCombinationMethod::Ignored } - _ => ShouldScorersCombinationMethod::Required(SpecializedScorer::Other( - scorer_disjunction( - should_scorers, - score_combiner_fn(), - effective_minimum_number_should_match, - ), + _ => ShouldScorersCombinationMethod::Required(scorer_disjunction( + should_scorers, + score_combiner_fn(), + effective_minimum_number_should_match, )), } }; @@ -290,13 +245,9 @@ impl BooleanWeight { let exclude_scorer_opt: Option> = if exclude_scorers.is_empty() { None } else { - let exclude_specialized_scorer: SpecializedScorer = + let exclude_scorers_union: Box = scorer_union(exclude_scorers, DoNothingCombiner::default, num_docs); - Some(into_box_scorer( - exclude_specialized_scorer, - DoNothingCombiner::default, - num_docs, - )) + Some(exclude_scorers_union) }; let include_scorer = match (should_scorers, must_scorers) { @@ -312,7 +263,7 @@ impl BooleanWeight { num_docs, ) .unwrap_or_else(|| Box::new(EmptyScorer)); - SpecializedScorer::Other(boxed_scorer) + boxed_scorer } (ShouldScorersCombinationMethod::Optional(should_scorer), must_scorers) => { // Optional SHOULD: contributes to scoring but not required for matching. @@ -337,16 +288,12 @@ impl BooleanWeight { Some(must_scorer) => { // Has MUST constraint: SHOULD only affects scoring. if self.scoring_enabled { - SpecializedScorer::Other(Box::new(RequiredOptionalScorer::< - _, - _, - TScoreCombiner, - >::new( + Box::new(RequiredOptionalScorer::<_, _, TScoreCombiner>::new( must_scorer, - into_box_scorer(should_scorer, &score_combiner_fn, num_docs), - ))) + should_scorer, + )) } else { - SpecializedScorer::Other(must_scorer) + must_scorer } } } @@ -366,23 +313,13 @@ impl BooleanWeight { } Some(must_scorer) => { // Has MUST constraint: intersect MUST with SHOULD. - let should_boxed = - into_box_scorer(should_scorer, &score_combiner_fn, num_docs); - SpecializedScorer::Other(intersect_scorers( - vec![must_scorer, should_boxed], - num_docs, - )) + intersect_scorers(vec![must_scorer, should_scorer], num_docs) } } } }; if let Some(exclude_scorer) = exclude_scorer_opt { - let include_scorer_boxed = - into_box_scorer(include_scorer, &score_combiner_fn, num_docs); - Ok(SpecializedScorer::Other(Box::new(Exclude::new( - include_scorer_boxed, - exclude_scorer, - )))) + Ok(Box::new(Exclude::new(include_scorer, exclude_scorer))) } else { Ok(include_scorer) } @@ -415,7 +352,6 @@ fn remove_and_count_all_and_empty_scorers( impl Weight for BooleanWeight { fn scorer(&self, reader: &SegmentReader, boost: Score) -> crate::Result> { - let num_docs = reader.num_docs(); if self.weights.is_empty() { Ok(Box::new(EmptyScorer)) } else if self.weights.len() == 1 { @@ -427,14 +363,8 @@ impl Weight for BooleanWeight Weight for BooleanWeight crate::Result<()> { - let scorer = self.complex_scorer(reader, 1.0, &self.score_combiner_fn)?; - match scorer { - SpecializedScorer::TermUnion(term_scorers) => { - let mut union_scorer = BufferedUnionScorer::build( - term_scorers, - &self.score_combiner_fn, - reader.num_docs(), - ); - for_each_scorer(&mut union_scorer, callback); - } - SpecializedScorer::Other(mut scorer) => { - for_each_scorer(scorer.as_mut(), callback); - } - } + let mut scorer = self.complex_scorer(reader, 1.0, &self.score_combiner_fn)?; + for_each_scorer(scorer.as_mut(), callback); Ok(()) } @@ -485,22 +403,9 @@ impl Weight for BooleanWeight crate::Result<()> { - let scorer = self.complex_scorer(reader, 1.0, || DoNothingCombiner)?; + let mut scorer = self.complex_scorer(reader, 1.0, || DoNothingCombiner)?; let mut buffer = [0u32; COLLECT_BLOCK_BUFFER_LEN]; - - match scorer { - SpecializedScorer::TermUnion(term_scorers) => { - let mut union_scorer = BufferedUnionScorer::build( - term_scorers, - &self.score_combiner_fn, - reader.num_docs(), - ); - for_each_docset_buffered(&mut union_scorer, &mut buffer, callback); - } - SpecializedScorer::Other(mut scorer) => { - for_each_docset_buffered(scorer.as_mut(), &mut buffer, callback); - } - } + for_each_docset_buffered(scorer.as_mut(), &mut buffer, callback); Ok(()) } @@ -520,16 +425,8 @@ impl Weight for BooleanWeight Score, ) -> crate::Result<()> { - let scorer = self.complex_scorer(reader, 1.0, &self.score_combiner_fn)?; - match scorer { - SpecializedScorer::TermUnion(_term_scorers) => { - // super::block_wand(term_scorers, threshold, callback); - todo!(); - } - SpecializedScorer::Other(mut scorer) => { - for_each_pruning_scorer(scorer.as_mut(), threshold, callback); - } - } + let mut scorer = self.complex_scorer(reader, 1.0, &self.score_combiner_fn)?; + scorer.for_each_pruning(threshold, callback); Ok(()) } } diff --git a/src/query/phrase_query/phrase_weight.rs b/src/query/phrase_query/phrase_weight.rs index aef345da1..ebb5cf708 100644 --- a/src/query/phrase_query/phrase_weight.rs +++ b/src/query/phrase_query/phrase_weight.rs @@ -2,9 +2,10 @@ use super::PhraseScorer; use crate::fieldnorm::FieldNormReader; use crate::index::SegmentReader; use crate::query::bm25::Bm25Weight; +use crate::query::explanation::does_not_match; use crate::query::{EmptyScorer, Explanation, Scorer, Weight}; use crate::schema::{IndexRecordOption, Term}; -use crate::{DocId, Score}; +use crate::{DocId, DocSet, Score}; pub struct PhraseWeight { phrase_terms: Vec<(usize, Term)>, @@ -19,11 +20,10 @@ impl PhraseWeight { phrase_terms: Vec<(usize, Term)>, similarity_weight_opt: Option, ) -> PhraseWeight { - let slop = 0; PhraseWeight { phrase_terms, similarity_weight_opt, - slop, + slop: 0, } } @@ -81,7 +81,7 @@ impl Weight for PhraseWeight { } } - fn explain(&self, _reader: &SegmentReader, _doc: DocId) -> crate::Result { + fn explain(&self, reader: &SegmentReader, doc: DocId) -> crate::Result { todo!(); // let scorer_opt = self.phrase_scorer(reader, 1.0)?; // if scorer_opt.is_none() { diff --git a/src/query/scorer.rs b/src/query/scorer.rs index e91fc2fbc..4d83098dc 100644 --- a/src/query/scorer.rs +++ b/src/query/scorer.rs @@ -3,7 +3,7 @@ use std::ops::DerefMut; use downcast_rs::impl_downcast; use crate::docset::DocSet; -use crate::Score; +use crate::{DocId, Score, TERMINATED}; /// Scored set of documents matching a query within a specific segment. /// @@ -13,6 +13,24 @@ pub trait Scorer: downcast_rs::Downcast + DocSet + 'static { /// /// This method will perform a bit of computation and is not cached. fn score(&mut self) -> Score; + + /// Calls `callback` with all of the `(doc, score)` for which score + /// is exceeding a given threshold. + /// + /// This method is useful for the TopDocs collector. + /// For all docsets, the blanket implementation has the benefit + /// of prefiltering (doc, score) pairs, avoiding the + /// virtual dispatch cost. + /// + /// More importantly, it makes it possible for scorers to implement + /// important optimization (e.g. BlockWAND for union). + fn for_each_pruning( + &mut self, + threshold: Score, + callback: &mut dyn FnMut(DocId, Score) -> Score, + ) { + for_each_pruning_scorer_default_impl(self, threshold, callback); + } } impl_downcast!(Scorer); @@ -23,3 +41,28 @@ impl Scorer for Box { self.deref_mut().score() } } + +/// Calls `callback` with all of the `(doc, score)` for which score +/// is exceeding a given threshold. +/// +/// This method is useful for the [`TopDocs`](crate::collector::TopDocs) collector. +/// For all docsets, the blanket implementation has the benefit +/// of prefiltering (doc, score) pairs, avoiding the +/// virtual dispatch cost. +/// +/// More importantly, it makes it possible for scorers to implement +/// important optimization (e.g. BlockWAND for union). +pub(crate) fn for_each_pruning_scorer_default_impl( + scorer: &mut TScorer, + mut threshold: Score, + callback: &mut dyn FnMut(DocId, Score) -> Score, +) { + let mut doc = scorer.doc(); + while doc != TERMINATED { + let score = scorer.score(); + if score > threshold { + threshold = callback(doc, score); + } + doc = scorer.advance(); + } +} diff --git a/src/query/term_query/term_scorer.rs b/src/query/term_query/term_scorer.rs index f756c6b14..dc2f3a8fe 100644 --- a/src/query/term_query/term_scorer.rs +++ b/src/query/term_query/term_scorer.rs @@ -2,7 +2,7 @@ use crate::codec::postings::PostingsCodec; use crate::codec::{Codec, StandardCodec}; use crate::docset::DocSet; use crate::fieldnorm::FieldNormReader; -use crate::postings::Postings; +use crate::postings::{Postings, PostingsWithBlockMax}; use crate::query::bm25::Bm25Weight; use crate::query::{Explanation, Scorer}; use crate::{DocId, Score}; @@ -51,8 +51,10 @@ impl TermScorer { pub fn max_score(&self) -> Score { self.similarity_weight.max_score() } +} - pub fn last_doc_in_block(&self) -> DocId { +impl TermScorer { + pub(crate) fn last_doc_in_block(&self) -> DocId { self.postings.last_doc_in_block() } diff --git a/src/query/term_query/term_weight.rs b/src/query/term_query/term_weight.rs index 9f75255ea..2d4ae49e0 100644 --- a/src/query/term_query/term_weight.rs +++ b/src/query/term_query/term_weight.rs @@ -121,13 +121,9 @@ impl Weight for TermWeight { ) -> crate::Result<()> { let specialized_scorer = self.specialized_scorer(reader, 1.0)?; match specialized_scorer { - TermOrEmptyOrAllScorer::TermScorer(term_scorer) => { - todo!(); - // crate::query::boolean_query::block_wand_single_scorer( - // *term_scorer, - // threshold, - // callback, - // ); + TermOrEmptyOrAllScorer::TermScorer(mut term_scorer) => { + // TODO re add blockwand + term_scorer.for_each_pruning(threshold, callback); } TermOrEmptyOrAllScorer::Empty => {} TermOrEmptyOrAllScorer::AllMatch(_) => { diff --git a/src/query/union/buffered_union.rs b/src/query/union/buffered_union.rs index ee554e357..a184bc889 100644 --- a/src/query/union/buffered_union.rs +++ b/src/query/union/buffered_union.rs @@ -110,22 +110,21 @@ impl BufferedUnionScorer bool { - if let Some(min_doc) = self.docsets.iter().map(DocSet::doc).min() { - // Reset the sliding window to start at the smallest doc - // across all scorers and prebuffer within the horizon. - self.window_start_doc = min_doc; - self.bucket_idx = 0; - self.doc = min_doc; - refill( - &mut self.docsets, - &mut self.bitsets, - &mut self.scores, - min_doc, - ); - true - } else { - false - } + let Some(min_doc) = self.docsets.iter().map(DocSet::doc).min() else { + return false; + }; + // Reset the sliding window to start at the smallest doc + // across all scorers and prebuffer within the horizon. + self.window_start_doc = min_doc; + self.bucket_idx = 0; + self.doc = min_doc; + refill( + &mut self.docsets, + &mut self.bitsets, + &mut self.scores, + min_doc, + ); + true } #[inline] diff --git a/src/query/weight.rs b/src/query/weight.rs index 23ff55c04..848c96298 100644 --- a/src/query/weight.rs +++ b/src/query/weight.rs @@ -34,31 +34,6 @@ pub(crate) fn for_each_docset_buffered( } } -/// Calls `callback` with all of the `(doc, score)` for which score -/// is exceeding a given threshold. -/// -/// This method is useful for the [`TopDocs`](crate::collector::TopDocs) collector. -/// For all docsets, the blanket implementation has the benefit -/// of prefiltering (doc, score) pairs, avoiding the -/// virtual dispatch cost. -/// -/// More importantly, it makes it possible for scorers to implement -/// important optimization (e.g. BlockWAND for union). -pub(crate) fn for_each_pruning_scorer( - scorer: &mut TScorer, - mut threshold: Score, - callback: &mut dyn FnMut(DocId, Score) -> Score, -) { - let mut doc = scorer.doc(); - while doc != TERMINATED { - let score = scorer.score(); - if score > threshold { - threshold = callback(doc, score); - } - doc = scorer.advance(); - } -} - /// A Weight is the specialization of a `Query` /// for a given set of segments. /// @@ -120,6 +95,7 @@ pub trait Weight: Send + Sync + 'static { /// /// More importantly, it makes it possible for scorers to implement /// important optimization (e.g. BlockWAND for union). + // TODO remove and move to scorer? fn for_each_pruning( &self, threshold: Score, @@ -127,7 +103,7 @@ pub trait Weight: Send + Sync + 'static { callback: &mut dyn FnMut(DocId, Score) -> Score, ) -> crate::Result<()> { let mut scorer = self.scorer(reader, 1.0)?; - for_each_pruning_scorer(scorer.as_mut(), threshold, callback); + scorer.for_each_pruning(threshold, callback); Ok(()) } }