use core::SegmentReader; use downcast::Downcast; use query::intersect_scorers; use query::score_combiner::{DoNothingCombiner, ScoreCombiner, SumWithCoordsCombiner}; use query::term_query::TermScorer; use query::EmptyScorer; use query::Exclude; use query::Occur; use query::RequiredOptionalScorer; use query::Scorer; use query::Union; use query::Weight; use std::borrow::Borrow; use std::collections::HashMap; use Result; fn scorer_union(scorers: Vec>) -> Box where TScoreCombiner: ScoreCombiner, { assert!(!scorers.is_empty()); if scorers.len() == 1 { return scorers.into_iter().next().unwrap(); //< we checked the size beforehands } { let is_all_term_queries = scorers.iter().all(|scorer| { let scorer_ref: &Scorer = scorer.borrow(); Downcast::::is_type(scorer_ref) }); if is_all_term_queries { let scorers: Vec = scorers .into_iter() .map(|scorer| *Downcast::::downcast(scorer).unwrap()) .collect(); let scorer: Box = Box::new(Union::::from(scorers)); return scorer; } } let scorer: Box = Box::new(Union::<_, TScoreCombiner>::from(scorers)); return scorer; } pub struct BooleanWeight { weights: Vec<(Occur, Box)>, scoring_enabled: bool, } impl BooleanWeight { pub fn new(weights: Vec<(Occur, Box)>, scoring_enabled: bool) -> BooleanWeight { BooleanWeight { weights, scoring_enabled, } } fn complex_scorer( &self, reader: &SegmentReader, ) -> Result> { let mut per_occur_scorers: HashMap>> = HashMap::new(); for &(ref occur, ref subweight) in &self.weights { let sub_scorer: Box = subweight.scorer(reader)?; per_occur_scorers .entry(*occur) .or_insert_with(Vec::new) .push(sub_scorer); } let should_scorer_opt: Option> = per_occur_scorers .remove(&Occur::Should) .map(scorer_union::); let exclude_scorer_opt: Option> = per_occur_scorers .remove(&Occur::MustNot) .map(scorer_union::); let must_scorer_opt: Option> = per_occur_scorers .remove(&Occur::Must) .map(intersect_scorers); let positive_scorer: Box = match (should_scorer_opt, must_scorer_opt) { (Some(should_scorer), Some(must_scorer)) => { if self.scoring_enabled { Box::new(RequiredOptionalScorer::<_, _, TScoreCombiner>::new( must_scorer, should_scorer, )) } else { must_scorer } } (None, Some(must_scorer)) => must_scorer, (Some(should_scorer), None) => should_scorer, (None, None) => { return Ok(Box::new(EmptyScorer)); } }; if let Some(exclude_scorer) = exclude_scorer_opt { Ok(Box::new(Exclude::new(positive_scorer, exclude_scorer))) } else { Ok(positive_scorer) } } } impl Weight for BooleanWeight { fn scorer(&self, reader: &SegmentReader) -> Result> { if self.weights.is_empty() { Ok(Box::new(EmptyScorer)) } else if self.weights.len() == 1 { let &(occur, ref weight) = &self.weights[0]; if occur == Occur::MustNot { Ok(Box::new(EmptyScorer)) } else { weight.scorer(reader) } } else if self.scoring_enabled { self.complex_scorer::(reader) } else { self.complex_scorer::(reader) } } }