hermes_core/query/
boost.rs1use std::sync::Arc;
4
5use crate::segment::SegmentReader;
6use crate::{DocId, Score};
7
8use super::{CountFuture, Query, Scorer, ScorerFuture};
9
10#[derive(Clone)]
12pub struct BoostQuery {
13 pub inner: Arc<dyn Query>,
14 pub boost: f32,
15}
16
17impl std::fmt::Debug for BoostQuery {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 f.debug_struct("BoostQuery")
20 .field("boost", &self.boost)
21 .finish()
22 }
23}
24
25impl std::fmt::Display for BoostQuery {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 write!(f, "{}^{}", self.inner, self.boost)
28 }
29}
30
31impl BoostQuery {
32 pub fn new(query: impl Query + 'static, boost: f32) -> Self {
33 Self {
34 inner: Arc::new(query),
35 boost,
36 }
37 }
38}
39
40impl Query for BoostQuery {
41 fn scorer<'a>(&self, reader: &'a SegmentReader, limit: usize) -> ScorerFuture<'a> {
42 self.scorer_with_options(reader, limit, super::ScorerOptions::with_positions())
43 }
44
45 fn scorer_with_options<'a>(
46 &self,
47 reader: &'a SegmentReader,
48 limit: usize,
49 options: super::ScorerOptions,
50 ) -> ScorerFuture<'a> {
51 let inner = self.inner.clone();
52 let boost = self.boost;
53 Box::pin(async move {
54 if !boost.is_finite() {
55 return Err(crate::Error::Query(
56 "boost must be a finite number".to_string(),
57 ));
58 }
59 let inner_options = if boost == 1.0 {
60 options
61 } else {
62 options.without_threshold()
63 };
64 let inner_scorer = inner
65 .scorer_with_options(reader, limit, inner_options)
66 .await?;
67 Ok(Box::new(BoostScorer {
68 inner: inner_scorer,
69 boost,
70 }) as Box<dyn Scorer + 'a>)
71 })
72 }
73
74 #[cfg(feature = "sync")]
75 fn scorer_sync<'a>(
76 &self,
77 reader: &'a SegmentReader,
78 limit: usize,
79 ) -> crate::Result<Box<dyn Scorer + 'a>> {
80 self.scorer_sync_with_options(reader, limit, super::ScorerOptions::with_positions())
81 }
82
83 #[cfg(feature = "sync")]
84 fn scorer_sync_with_options<'a>(
85 &self,
86 reader: &'a SegmentReader,
87 limit: usize,
88 options: super::ScorerOptions,
89 ) -> crate::Result<Box<dyn Scorer + 'a>> {
90 if !self.boost.is_finite() {
91 return Err(crate::Error::Query(
92 "boost must be a finite number".to_string(),
93 ));
94 }
95 let inner_options = if self.boost == 1.0 {
96 options
97 } else {
98 options.without_threshold()
99 };
100 let inner_scorer = self
101 .inner
102 .scorer_sync_with_options(reader, limit, inner_options)?;
103 Ok(Box::new(BoostScorer {
104 inner: inner_scorer,
105 boost: self.boost,
106 }) as Box<dyn Scorer + 'a>)
107 }
108
109 fn count_estimate<'a>(&self, reader: &'a SegmentReader) -> CountFuture<'a> {
110 let inner = self.inner.clone();
111 Box::pin(async move { inner.count_estimate(reader).await })
112 }
113
114 fn is_filter(&self) -> bool {
115 self.boost == 1.0 && self.inner.is_filter()
116 }
117
118 fn as_doc_predicate<'a>(&self, reader: &'a SegmentReader) -> Option<super::DocPredicate<'a>> {
119 (self.boost == 1.0)
120 .then(|| self.inner.as_doc_predicate(reader))
121 .flatten()
122 }
123
124 fn text_terms(&self, out: &mut Vec<(crate::dsl::Field, Vec<u8>)>) {
125 self.inner.text_terms(out);
126 }
127
128 fn decompose(&self) -> super::QueryDecomposition {
129 match self.inner.decompose() {
130 super::QueryDecomposition::TextTerm(mut info) => {
133 info.weight *= self.boost;
134 super::QueryDecomposition::TextTerm(info)
135 }
136 other if self.boost == 1.0 => other,
137 _ => super::QueryDecomposition::Opaque,
138 }
139 }
140}
141
142struct BoostScorer<'a> {
143 inner: Box<dyn Scorer + 'a>,
144 boost: f32,
145}
146
147impl super::docset::DocSet for BoostScorer<'_> {
148 fn doc(&self) -> DocId {
149 self.inner.doc()
150 }
151
152 fn advance(&mut self) -> DocId {
153 self.inner.advance()
154 }
155
156 fn seek(&mut self, target: DocId) -> DocId {
157 self.inner.seek(target)
158 }
159
160 fn size_hint(&self) -> u32 {
161 self.inner.size_hint()
162 }
163}
164
165impl Scorer for BoostScorer<'_> {
166 fn score(&self) -> Score {
167 self.inner.score() * self.boost
168 }
169
170 fn matched_positions(&self) -> Option<super::MatchedPositions> {
171 let mut positions = self.inner.matched_positions()?;
172 for (_, scored_positions) in &mut positions {
173 for position in scored_positions {
174 position.score *= self.boost;
175 }
176 }
177 Some(positions)
178 }
179}